Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 <tt>FC_DURATION_MSG, FC_DURATION_TRACE</tt> as usual.
|
||||
* You can also use <tt>FC_DURATION_MSG, FC_DURATION_TRACE</tt> 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2013 Juergen Riegel <FreeCAD@juergen-riegel.net> *
|
||||
# * Copyright (c) 2013 Juergen Riegel <FreeCAD@juergen-riegel.net> *
|
||||
# * Copyright (c) 2016 Bernd Hahnebach <bernd@bimstatik.org> *
|
||||
# * *
|
||||
# * 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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -17,77 +17,70 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonURL">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Opens the Product URL of this material in an external browser</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Material card:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="ComboMaterial">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Existing material cards</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonOpen">
|
||||
<property name="toolTip">
|
||||
<string>Opens an existing material card</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonSave">
|
||||
<property name="toolTip">
|
||||
<string>Saves this material as a card</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save as...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Material card</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonURL">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Opens the Product URL of this material in an external browser</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="ComboMaterial">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Existing material cards</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonOpen">
|
||||
<property name="toolTip">
|
||||
<string>Opens an existing material card</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ButtonSave">
|
||||
<property name="toolTip">
|
||||
<string>Saves this material as a card</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save as...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="PreviewGroup">
|
||||
@@ -138,19 +131,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Material parameter</string>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Add / Remove</string>
|
||||
<string>Add / remove parameter</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
|
||||
@@ -1746,6 +1746,24 @@ std::set<unsigned long> MeshRefPointToFacets::NeighbourPoints(const std::vector<
|
||||
return nb;
|
||||
}
|
||||
|
||||
std::set<unsigned long> MeshRefPointToFacets::NeighbourPoints(unsigned long pos) const
|
||||
{
|
||||
std::set<unsigned long> p;
|
||||
const std::set<unsigned long>& vf = _map[pos];
|
||||
for (std::set<unsigned long>::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<unsigned long> visited;
|
||||
|
||||
@@ -375,6 +375,7 @@ public:
|
||||
const std::set<unsigned long>& operator[] (unsigned long) const;
|
||||
MeshFacetArray::_TConstIterator GetFacet (unsigned long) const;
|
||||
std::set<unsigned long> NeighbourPoints(const std::vector<unsigned long>& , int level) const;
|
||||
std::set<unsigned long> 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);
|
||||
|
||||
@@ -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<unsigned long> 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) {
|
||||
|
||||
@@ -75,6 +75,8 @@ struct MeshExport EdgeCollapse
|
||||
{
|
||||
unsigned long _fromPoint;
|
||||
unsigned long _toPoint;
|
||||
std::vector<unsigned long> _adjacentFrom; // adjacent points to _fromPoint
|
||||
std::vector<unsigned long> _adjacentTo; // adjacent points to _toPoint
|
||||
std::vector<unsigned long> _removeFacets;
|
||||
std::vector<unsigned long> _changeFacets;
|
||||
};
|
||||
|
||||
@@ -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<unsigned long> commonPoints;
|
||||
std::set_intersection(ec._adjacentFrom.begin(), ec._adjacentFrom.end(),
|
||||
ec._adjacentTo.begin(), ec._adjacentTo.end(),
|
||||
std::back_insert_iterator<std::vector<unsigned long> >(commonPoints));
|
||||
if (commonPoints.size() > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check geometry
|
||||
std::vector<unsigned long>::const_iterator it;
|
||||
for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) {
|
||||
MeshFacet f = _rclMesh._aclFacetArray[*it];
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="PersistencePy"
|
||||
Name="ConstraintPy"
|
||||
Twin="Constraint"
|
||||
TwinPointer="Constraint"
|
||||
Include="Mod/Sketcher/App/Constraint.h"
|
||||
Namespace="Sketcher"
|
||||
FatherInclude="Base/PersistencePy.h"
|
||||
<PythonExport
|
||||
Father="PersistencePy"
|
||||
Name="ConstraintPy"
|
||||
Twin="Constraint"
|
||||
TwinPointer="Constraint"
|
||||
Include="Mod/Sketcher/App/Constraint.h"
|
||||
Namespace="Sketcher"
|
||||
FatherInclude="Base/PersistencePy.h"
|
||||
FatherNamespace="Base"
|
||||
Constructor="true"
|
||||
Delete="true">
|
||||
@@ -28,7 +28,7 @@
|
||||
</Documentation>
|
||||
<Parameter Name="First" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="FirstPos" ReadOnly="true">
|
||||
<Attribute Name="FirstPos" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Position of first geometry index the Constraint refers to</UserDocu>
|
||||
</Documentation>
|
||||
@@ -40,7 +40,7 @@
|
||||
</Documentation>
|
||||
<Parameter Name="Second" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="SecondPos" ReadOnly="true">
|
||||
<Attribute Name="SecondPos" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Position of second geometry index the Constraint refers to</UserDocu>
|
||||
</Documentation>
|
||||
@@ -52,7 +52,7 @@
|
||||
</Documentation>
|
||||
<Parameter Name="Second" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="ThirdPos" ReadOnly="true">
|
||||
<Attribute Name="ThirdPos" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Position of third geometry index the Constraint refers to</UserDocu>
|
||||
</Documentation>
|
||||
|
||||
@@ -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<int>(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<int>(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<int>(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;
|
||||
}
|
||||
|
||||
@@ -1488,10 +1488,16 @@ int Sketch::addConstraint(const Constraint *constraint)
|
||||
int Sketch::addConstraints(const std::vector<Constraint *> &ConstraintList)
|
||||
{
|
||||
int rtn = -1;
|
||||
int cid = 0;
|
||||
|
||||
for (std::vector<Constraint *>::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++it)
|
||||
for (std::vector<Constraint *>::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<Constraint *> &ConstraintList,
|
||||
for (std::vector<Constraint *>::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
|
||||
|
||||
@@ -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<Eigen::MatrixXd>::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<double,-1,-1,0,-1,-1> MatrixdType;
|
||||
typedef Matrix<double,-1,-1,0,-1,-1> MatrixdType;
|
||||
|
||||
template<>
|
||||
FullPivLU<MatrixdType>& FullPivLU<MatrixdType>::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<Scalar>::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<MatrixdType>& FullPivLU<MatrixdType>::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<Scalar>::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<rows-1)
|
||||
m_lu.col(k).tail(rows-k-1) /= m_lu.coeff(k,k);
|
||||
if(k<size-1)
|
||||
m_lu.block(k+1,k+1,rows-k-1,cols-k-1).noalias() -= m_lu.col(k).tail(rows-k-1) * m_lu.row(k).tail(cols-k-1);
|
||||
}
|
||||
|
||||
// the main loop is over, we still have to accumulate the transpositions to find the
|
||||
// permutations P and Q
|
||||
|
||||
m_p.setIdentity(rows);
|
||||
for(Index k = size-1; 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<rows-1)
|
||||
m_lu.col(k).tail(rows-k-1) /= m_lu.coeff(k,k);
|
||||
if(k<size-1)
|
||||
m_lu.block(k+1,k+1,rows-k-1,cols-k-1).noalias() -= m_lu.col(k).tail(rows-k-1) * m_lu.row(k).tail(cols-k-1);
|
||||
}
|
||||
|
||||
// the main loop is over, we still have to accumulate the transpositions to find the
|
||||
// permutations P and Q
|
||||
|
||||
m_p.setIdentity(rows);
|
||||
for(Index k = size-1; 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 <boost::vecS, boost::vecS, boost::undirectedS> 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<int,int> &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<Constraint *>::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<int,int> jacobianconstraintmap;
|
||||
|
||||
int jacobianconstraintcount=0;
|
||||
int allcount=0;
|
||||
for (std::vector<Constraint *>::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<double> 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<int>(it->second.size()) > maxPopularity ||
|
||||
(static_cast<int>(it->second.size()) == maxPopularity && mostPopular &&
|
||||
tagmultiplicity[it->first->getTag()] < tagmultiplicity[mostPopular->getTag()]) ||
|
||||
tagmultiplicity.at(it->first->getTag()) < tagmultiplicity.at(mostPopular->getTag())) ||
|
||||
|
||||
(static_cast<int>(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();
|
||||
|
||||
@@ -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<Constraint *> 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<int,int> &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<Constraint *> 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);
|
||||
|
||||
@@ -1749,7 +1749,10 @@ std::set<int> 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)) {
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
# include <cstring>
|
||||
# include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
# include <exception>
|
||||
# include <boost/regex.hpp>
|
||||
# include <QString>
|
||||
# include <QStringList>
|
||||
# include <QRegExp>
|
||||
#include <QChar>
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
@@ -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<std::string::const_iterator> 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)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QPointF>
|
||||
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
@@ -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);
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <QLocale>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Parameter.h>
|
||||
@@ -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<std::string> &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<std::string> &subElements = References2D.getSubValues();
|
||||
|
||||
@@ -643,7 +647,7 @@ pointPair DrawViewDimension::getPointsOneEdge()
|
||||
if (geom && geom->geomType == TechDrawGeometry::GeomType::GENERIC) {
|
||||
gen = static_cast<TechDrawGeometry::Generic*>(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<std::string> &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<std::string> &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<std::string> &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<App::DocumentObject*> &objects = References2D.getValues();
|
||||
const std::vector<std::string> &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<App::DocumentObject*> &objects = References2D.getValues();
|
||||
if (!objects.empty()) {
|
||||
const std::vector<std::string> &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<App::DocumentObject*> &objects = References2D.getValues();
|
||||
const std::vector<std::string> &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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<App::DocumentObject*>& links = Source.getValues();
|
||||
if (links.empty()) {
|
||||
@@ -203,6 +205,7 @@ TopoDS_Shape DrawViewPart::getSourceShape(void) const
|
||||
|
||||
std::vector<TopoDS_Shape> DrawViewPart::getShapesFromObject(App::DocumentObject* docObj) const
|
||||
{
|
||||
// Base::Console().Message("DVP::getShapesFromObject() - %s\n", getNameInDocument());
|
||||
std::vector<TopoDS_Shape> result;
|
||||
App::GroupExtension* gex = dynamic_cast<App::GroupExtension*>(docObj);
|
||||
App::Property* gProp = docObj->getPropertyByName("Group");
|
||||
@@ -250,6 +253,7 @@ std::vector<TopoDS_Shape> 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 <double, milli> (diff).count();
|
||||
Base::Console().Log("TIMING - %s DVP spent: %.3f millisecs in GO::extractGeometry\n",getNameInDocument(),diffOut);
|
||||
|
||||
const std::vector<TechDrawGeometry::BaseGeom *> & edges = go->getEdgeGeometry();
|
||||
if (edges.empty()) {
|
||||
Base::Console().Log("DVP::buildGO - NO extracted edges!\n");
|
||||
}
|
||||
bbox = go->calcBoundingBox();
|
||||
return go;
|
||||
}
|
||||
|
||||
@@ -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<BaseGeom *>::const_iterator it( edgeGeom.begin() );
|
||||
it != edgeGeom.end(); ++it) {
|
||||
BRepBndLib::Add((*it)->occEdge, testBox);
|
||||
if (!edgeGeom.empty()) {
|
||||
for (std::vector<BaseGeom *>::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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user