Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Patrick Felixberger
2019-04-25 18:38:00 +02:00
24 changed files with 1177 additions and 650 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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')

View File

@@ -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")

View File

@@ -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>

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;
};

View File

@@ -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];

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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 &params);

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;
}
}
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {