Merge branch 'master' of git://free-cad.git.sourceforge.net/gitroot/free-cad/free-cad
This commit is contained in:
@@ -579,34 +579,7 @@ void Document::Save (Base::Writer &writer) const
|
||||
PropertyContainer::Save(writer);
|
||||
|
||||
// writing the features types
|
||||
writer.incInd(); // indention for 'Objects count'
|
||||
writer.Stream() << writer.ind() << "<Objects Count=\"" << d->objectArray.size() <<"\">" << endl;
|
||||
|
||||
writer.incInd(); // indention for 'Object type'
|
||||
std::vector<DocumentObject*>::const_iterator it;
|
||||
for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
|
||||
writer.Stream() << writer.ind() << "<Object "
|
||||
<< "type=\"" << (*it)->getTypeId().getName() << "\" "
|
||||
<< "name=\"" << (*it)->getNameInDocument() << "\" "
|
||||
<< "/>" << endl;
|
||||
}
|
||||
|
||||
writer.decInd(); // indention for 'Object type'
|
||||
writer.Stream() << writer.ind() << "</Objects>" << endl;
|
||||
|
||||
// writing the features itself
|
||||
writer.Stream() << writer.ind() << "<ObjectData Count=\"" << d->objectArray.size() <<"\">" << endl;
|
||||
|
||||
writer.incInd(); // indention for 'Object name'
|
||||
for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
|
||||
writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getNameInDocument() << "\">" << endl;
|
||||
(*it)->Save(writer);
|
||||
writer.Stream() << writer.ind() << "</Object>" << endl;
|
||||
}
|
||||
|
||||
writer.decInd(); // indention for 'Object name'
|
||||
writer.Stream() << writer.ind() << "</ObjectData>" << endl;
|
||||
writer.decInd(); // indention for 'Objects count'
|
||||
writeObjects(d->objectArray, writer);
|
||||
writer.Stream() << "</Document>" << endl;
|
||||
}
|
||||
|
||||
@@ -685,37 +658,7 @@ void Document::Restore(Base::XMLReader &reader)
|
||||
} // SchemeVersion "3" or higher
|
||||
else if ( scheme >= 3 ) {
|
||||
// read the feature types
|
||||
reader.readElement("Objects");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
string type = reader.getAttribute("type");
|
||||
string name = reader.getAttribute("name");
|
||||
|
||||
try {
|
||||
addObject(type.c_str(),name.c_str());
|
||||
}
|
||||
catch ( Base::Exception& ) {
|
||||
Base::Console().Message("Cannot create object '%s'\n", name.c_str());
|
||||
}
|
||||
}
|
||||
reader.readEndElement("Objects");
|
||||
|
||||
// read the features itself
|
||||
reader.readElement("ObjectData");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
string name = reader.getAttribute("name");
|
||||
DocumentObject* pObj = getObject(name.c_str());
|
||||
if (pObj) { // check if this feature has been registered
|
||||
pObj->StatusBits.set(4);
|
||||
pObj->Restore(reader);
|
||||
pObj->StatusBits.reset(4);
|
||||
}
|
||||
reader.readEndElement("Object");
|
||||
}
|
||||
reader.readEndElement("ObjectData");
|
||||
readObjects(reader);
|
||||
}
|
||||
|
||||
reader.readEndElement("Document");
|
||||
@@ -732,6 +675,20 @@ void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
writer.Stream() << "<Properties Count=\"0\">" << endl;
|
||||
writer.Stream() << "</Properties>" << endl;
|
||||
|
||||
// writing the object types
|
||||
writeObjects(obj, writer);
|
||||
writer.Stream() << "</Document>" << endl;
|
||||
|
||||
// Hook for others to add further data.
|
||||
signalExportObjects(obj, writer);
|
||||
|
||||
// write additional files
|
||||
writer.writeFiles();
|
||||
}
|
||||
|
||||
void Document::writeObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
Base::Writer &writer) const
|
||||
{
|
||||
// writing the features types
|
||||
writer.incInd(); // indention for 'Objects count'
|
||||
writer.Stream() << writer.ind() << "<Objects Count=\"" << obj.size() <<"\">" << endl;
|
||||
@@ -741,7 +698,7 @@ void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
for (it = obj.begin(); it != obj.end(); ++it) {
|
||||
writer.Stream() << writer.ind() << "<Object "
|
||||
<< "type=\"" << (*it)->getTypeId().getName() << "\" "
|
||||
<< "name=\"" << (*it)->getNameInDocument() << "\" "
|
||||
<< "name=\"" << (*it)->getNameInDocument() << "\" "
|
||||
<< "/>" << endl;
|
||||
}
|
||||
|
||||
@@ -761,42 +718,31 @@ void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
|
||||
writer.decInd(); // indention for 'Object name'
|
||||
writer.Stream() << writer.ind() << "</ObjectData>" << endl;
|
||||
writer.decInd(); // indention for 'Objects count'
|
||||
writer.Stream() << "</Document>" << endl;
|
||||
|
||||
// Hook for others to add further data.
|
||||
signalExportObjects(obj, writer);
|
||||
|
||||
// write additional files
|
||||
writer.writeFiles();
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*>
|
||||
Document::importObjects(std::istream& input)
|
||||
Document::readObjects(Base::XMLReader& reader)
|
||||
{
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
zipios::ZipInputStream zipstream(input);
|
||||
Base::XMLReader reader("<memory>", zipstream);
|
||||
|
||||
int i,Cnt;
|
||||
reader.readElement("Document");
|
||||
long scheme = reader.getAttributeAsInteger("SchemaVersion");
|
||||
reader.DocumentSchema = scheme;
|
||||
|
||||
// read the object types
|
||||
std::map<std::string, std::string> nameMap;
|
||||
reader.readElement("Objects");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
int Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (int i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
string type = reader.getAttribute("type");
|
||||
string name = reader.getAttribute("name");
|
||||
std::string type = reader.getAttribute("type");
|
||||
std::string name = reader.getAttribute("name");
|
||||
|
||||
try {
|
||||
// Use name from XML as is and do NOT remove trailing digits because
|
||||
// otherwise we may cause a dependency to itself
|
||||
// Example: Object 'Cut001' references object 'Cut' and removing the
|
||||
// digits we make an object 'Cut' referencing itself.
|
||||
App::DocumentObject* o = addObject(type.c_str(),name.c_str());
|
||||
objs.push_back(o);
|
||||
// use this name for the later access because an object with
|
||||
// the given name may already exist
|
||||
nameMap[name] = o->getNameInDocument();
|
||||
reader.addName(name.c_str(), o->getNameInDocument());
|
||||
}
|
||||
catch (Base::Exception&) {
|
||||
Base::Console().Message("Cannot create object '%s'\n", name.c_str());
|
||||
@@ -807,9 +753,9 @@ Document::importObjects(std::istream& input)
|
||||
// read the features itself
|
||||
reader.readElement("ObjectData");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
for (int i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
std::string name = nameMap[reader.getAttribute("name")];
|
||||
std::string name = reader.getName(reader.getAttribute("name"));
|
||||
DocumentObject* pObj = getObject(name.c_str());
|
||||
if (pObj) { // check if this feature has been registered
|
||||
pObj->StatusBits.set(4);
|
||||
@@ -820,12 +766,26 @@ Document::importObjects(std::istream& input)
|
||||
}
|
||||
reader.readEndElement("ObjectData");
|
||||
|
||||
return objs;
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*>
|
||||
Document::importObjects(Base::XMLReader& reader)
|
||||
{
|
||||
reader.readElement("Document");
|
||||
long scheme = reader.getAttributeAsInteger("SchemaVersion");
|
||||
reader.DocumentSchema = scheme;
|
||||
|
||||
std::vector<App::DocumentObject*> objs = readObjects(reader);
|
||||
|
||||
reader.readEndElement("Document");
|
||||
signalImportObjects(objs, reader);
|
||||
reader.readFiles(zipstream);
|
||||
|
||||
// reset all touched
|
||||
for (std::vector<DocumentObject*>::iterator it= objs.begin();it!=objs.end();++it)
|
||||
for (std::vector<DocumentObject*>::iterator it= objs.begin();it!=objs.end();++it) {
|
||||
(*it)->onDocumentRestored();
|
||||
(*it)->purgeTouched();
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
void restore (void);
|
||||
void exportObjects(const std::vector<App::DocumentObject*>&, std::ostream&);
|
||||
void exportGraphviz(std::ostream&);
|
||||
std::vector<App::DocumentObject*> importObjects(std::istream&);
|
||||
std::vector<App::DocumentObject*> importObjects(Base::XMLReader& reader);
|
||||
/// Opens the document from its file name
|
||||
//void open (void);
|
||||
/// Is the document already saved to a file
|
||||
@@ -266,6 +266,8 @@ protected:
|
||||
/// checks if a valid transaction is open
|
||||
void _checkTransaction(void);
|
||||
void breakDependency(DocumentObject* pcObject, bool clear);
|
||||
std::vector<App::DocumentObject*> readObjects(Base::XMLReader& reader);
|
||||
void writeObjects(const std::vector<App::DocumentObject*>&, Base::Writer &writer) const;
|
||||
|
||||
void onChanged(const Property* prop);
|
||||
/// callback from the Document objects before property will be changed
|
||||
|
||||
@@ -360,6 +360,15 @@ bool Base::XMLReader::isRegistered(Base::Persistence *Object) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Base::XMLReader::addName(const char*, const char*)
|
||||
{
|
||||
}
|
||||
|
||||
const char* Base::XMLReader::getName(const char* name) const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Base::XMLReader: Implementation of the SAX DocumentHandler interface
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -159,6 +159,8 @@ public:
|
||||
/// get all registered file names
|
||||
const std::vector<std::string>& getFilenames() const;
|
||||
bool isRegistered(Base::Persistence *Object) const;
|
||||
virtual void addName(const char*, const char*);
|
||||
virtual const char* getName(const char*) const;
|
||||
//@}
|
||||
|
||||
/// Schema Version of the document
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
# include <QDesktopWidget>
|
||||
# include <QEvent>
|
||||
# include <QMessageBox>
|
||||
# include <QTimer>
|
||||
# include <QToolBar>
|
||||
# include <QToolButton>
|
||||
#endif
|
||||
@@ -55,13 +56,21 @@ using namespace Gui::Dialog;
|
||||
* Constructs an action called \a name with parent \a parent. It also stores a pointer
|
||||
* to the command object.
|
||||
*/
|
||||
Action::Action (Command* pcCmd,QObject * parent)
|
||||
Action::Action (Command* pcCmd, QObject * parent)
|
||||
: QObject(parent), _action(new QAction( this )), _pcCmd(pcCmd)
|
||||
{
|
||||
_action->setObjectName(QString::fromAscii(_pcCmd->getName()));
|
||||
connect(_action, SIGNAL(triggered(bool)), this, SLOT(onActivated()));
|
||||
}
|
||||
|
||||
Action::Action (Command* pcCmd, QAction* action, QObject * parent)
|
||||
: QObject(parent), _action(action), _pcCmd(pcCmd)
|
||||
{
|
||||
_action->setParent(this);
|
||||
_action->setObjectName(QString::fromAscii(_pcCmd->getName()));
|
||||
connect(_action, SIGNAL(triggered(bool)), this, SLOT(onActivated()));
|
||||
}
|
||||
|
||||
Action::~Action()
|
||||
{
|
||||
delete _action;
|
||||
@@ -257,6 +266,14 @@ void ActionGroup::setVisible( bool b )
|
||||
_group->setVisible(b);
|
||||
}
|
||||
|
||||
QAction* ActionGroup::addAction(QAction* action)
|
||||
{
|
||||
int index = _group->actions().size();
|
||||
action = _group->addAction(action);
|
||||
action->setData(QVariant(index));
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* ActionGroup::addAction(const QString& text)
|
||||
{
|
||||
int index = _group->actions().size();
|
||||
@@ -289,6 +306,14 @@ void ActionGroup::onActivated ()
|
||||
_pcCmd->invoke(this->property("defaultAction").toInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the command.
|
||||
*/
|
||||
void ActionGroup::onActivated (int index)
|
||||
{
|
||||
_pcCmd->invoke(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the command.
|
||||
*/
|
||||
@@ -410,6 +435,8 @@ void WorkbenchComboBox::onActivated(int i)
|
||||
int index = itemData(i).toInt();
|
||||
WorkbenchActionEvent* ev = new WorkbenchActionEvent(this->actions()[index]);
|
||||
QApplication::postEvent(this->group, ev);
|
||||
// TODO: Test if we can use this instead
|
||||
//QTimer::singleShot(20, this->actions()[i], SLOT(trigger()));
|
||||
}
|
||||
|
||||
void WorkbenchComboBox::onActivated(QAction* action)
|
||||
|
||||
@@ -45,6 +45,8 @@ class GuiExport Action : public QObject
|
||||
|
||||
public:
|
||||
Action (Command* pcCmd, QObject * parent = 0);
|
||||
/// Action takes ownership of the 'action' object.
|
||||
Action (Command* pcCmd, QAction* action, QObject * parent);
|
||||
virtual ~Action();
|
||||
|
||||
virtual void addTo (QWidget * w);
|
||||
@@ -100,6 +102,7 @@ public:
|
||||
void setVisible (bool);
|
||||
|
||||
void setDropDownMenu(bool b) { _dropDown = b; }
|
||||
QAction* addAction(QAction*);
|
||||
QAction* addAction(const QString&);
|
||||
QList<QAction*> actions() const;
|
||||
int checkedAction() const;
|
||||
@@ -107,6 +110,7 @@ public:
|
||||
|
||||
public Q_SLOTS:
|
||||
void onActivated ();
|
||||
void onActivated (int);
|
||||
void onActivated (QAction*);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -42,10 +42,22 @@ namespace Gui {
|
||||
class XMLMergeReader : public Base::XMLReader
|
||||
{
|
||||
public:
|
||||
XMLMergeReader(const std::map<std::string, std::string>& name, const char* FileName, std::istream& str)
|
||||
XMLMergeReader(std::map<std::string, std::string>& name, const char* FileName, std::istream& str)
|
||||
: Base::XMLReader(FileName, str), nameMap(name)
|
||||
{}
|
||||
|
||||
void addName(const char* s1, const char* s2)
|
||||
{
|
||||
nameMap[s1] = s2;
|
||||
}
|
||||
const char* getName(const char* name) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it = nameMap.find(name);
|
||||
if (it != nameMap.end())
|
||||
return it->second.c_str();
|
||||
else
|
||||
return name;
|
||||
}
|
||||
protected:
|
||||
void startElement(const XMLCh* const uri, const XMLCh* const localname,
|
||||
const XMLCh* const qname,
|
||||
@@ -75,7 +87,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
const std::map<std::string, std::string>& nameMap;
|
||||
std::map<std::string, std::string>& nameMap;
|
||||
typedef std::pair<std::string, std::string> PropertyTag;
|
||||
std::stack<PropertyTag> propertyStack;
|
||||
};
|
||||
@@ -104,64 +116,14 @@ unsigned int MergeDocuments::getMemSize (void) const
|
||||
std::vector<App::DocumentObject*>
|
||||
MergeDocuments::importObjects(std::istream& input)
|
||||
{
|
||||
//std::map<std::string, std::string> nameMap;
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
zipios::ZipInputStream zipstream(input);
|
||||
XMLMergeReader reader(nameMap,"<memory>", zipstream);
|
||||
this->nameMap.clear();
|
||||
this->stream = new zipios::ZipInputStream(input);
|
||||
XMLMergeReader reader(this->nameMap,"<memory>", *stream);
|
||||
std::vector<App::DocumentObject*> objs = appdoc->importObjects(reader);
|
||||
|
||||
int i,Cnt;
|
||||
reader.readElement("Document");
|
||||
long scheme = reader.getAttributeAsInteger("SchemaVersion");
|
||||
reader.DocumentSchema = scheme;
|
||||
delete this->stream;
|
||||
this->stream = 0;
|
||||
|
||||
// read the object types
|
||||
nameMap.clear();
|
||||
reader.readElement("Objects");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
std::string type = reader.getAttribute("type");
|
||||
std::string name = reader.getAttribute("name");
|
||||
|
||||
try {
|
||||
// Use name from XML as is and do NOT remove trailing digits because
|
||||
// otherwise we may cause a dependency to itself
|
||||
// Example: Object 'Cut001' references object 'Cut' and removing the
|
||||
// digits we make an object 'Cut' referencing itself.
|
||||
App::DocumentObject* o = appdoc->addObject(type.c_str(),name.c_str());
|
||||
objs.push_back(o);
|
||||
// use this name for the later access because an object with
|
||||
// the given name may already exist
|
||||
nameMap[name] = o->getNameInDocument();
|
||||
}
|
||||
catch (Base::Exception&) {
|
||||
Base::Console().Message("Cannot create object '%s'\n", name.c_str());
|
||||
}
|
||||
}
|
||||
reader.readEndElement("Objects");
|
||||
|
||||
// read the features itself
|
||||
reader.readElement("ObjectData");
|
||||
Cnt = reader.getAttributeAsInteger("Count");
|
||||
for (i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Object");
|
||||
std::string name = nameMap[reader.getAttribute("name")];
|
||||
App::DocumentObject* pObj = appdoc->getObject(name.c_str());
|
||||
if (pObj) { // check if this feature has been registered
|
||||
// pObj->StatusBits.set(4);
|
||||
pObj->Restore(reader);
|
||||
// pObj->StatusBits.reset(4);
|
||||
}
|
||||
reader.readEndElement("Object");
|
||||
}
|
||||
reader.readEndElement("ObjectData");
|
||||
|
||||
reader.readEndElement("Document");
|
||||
appdoc->signalImportObjects(objs, reader);
|
||||
reader.readFiles(zipstream);
|
||||
// reset all touched
|
||||
for (std::vector<App::DocumentObject*>::iterator it= objs.begin();it!=objs.end();++it)
|
||||
(*it)->purgeTouched();
|
||||
return objs;
|
||||
}
|
||||
|
||||
@@ -173,6 +135,8 @@ void MergeDocuments::importObject(const std::vector<App::DocumentObject*>& o, Ba
|
||||
if (vp) vp->hide();
|
||||
}
|
||||
Restore(r);
|
||||
|
||||
r.readFiles(*this->stream);
|
||||
}
|
||||
|
||||
void MergeDocuments::exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer & w)
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include <boost/signals.hpp>
|
||||
#include <Base/Persistence.h>
|
||||
|
||||
namespace zipios {
|
||||
class ZipInputStream;
|
||||
}
|
||||
namespace App {
|
||||
class Document;
|
||||
class DocumentObject;
|
||||
@@ -49,6 +52,7 @@ public:
|
||||
void RestoreDocFile(Base::Reader & r);
|
||||
|
||||
private:
|
||||
zipios::ZipInputStream* stream;
|
||||
App::Document* appdoc;
|
||||
Gui::Document* document;
|
||||
std::vector<App::DocumentObject*> objects;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <QMessageBox>
|
||||
#endif
|
||||
|
||||
#include "TaskDialog.h"
|
||||
@@ -57,6 +58,20 @@ const std::vector<QWidget*> &TaskDialog::getDialogContent(void) const
|
||||
return Content;
|
||||
}
|
||||
|
||||
bool TaskDialog::canClose() const
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(tr("A dialog is already open in the task panel"));
|
||||
msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?"));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
int ret = msgBox.exec();
|
||||
if (ret == QMessageBox::Yes)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//==== calls from the TaskView ===============================================================
|
||||
|
||||
void TaskDialog::open()
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
ButtonPosition buttonPosition() const
|
||||
{ return pos; }
|
||||
const std::vector<QWidget*> &getDialogContent(void) const;
|
||||
bool canClose() const;
|
||||
|
||||
/// tells the framework which buttons whisched for the dialog
|
||||
virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const
|
||||
|
||||
@@ -1378,6 +1378,11 @@ void View3DInventorViewer::viewAll()
|
||||
group->mode = SoSkipBoundingGroup::EXCLUDE_BBOX;
|
||||
}
|
||||
|
||||
// Set the height angle to 45 deg
|
||||
SoCamera* cam = this->getCamera();
|
||||
if (cam && cam->getTypeId().isDerivedFrom(SoPerspectiveCamera::getClassTypeId()))
|
||||
static_cast<SoPerspectiveCamera*>(cam)->heightAngle = (float)(M_PI / 4.0);
|
||||
|
||||
// call the default implementation first to make sure everything is visible
|
||||
SoQtViewer::viewAll();
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
# include <QApplication>
|
||||
# include <QEvent>
|
||||
# include <QFileInfo>
|
||||
# include <QPixmap>
|
||||
# include <boost/signals.hpp>
|
||||
@@ -68,7 +70,19 @@ using namespace Gui;
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class ViewProviderPythonFeatureObserver
|
||||
class PropertyEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
PropertyEvent(App::Property* p1, App::Property* p2)
|
||||
: QEvent(QEvent::Type(QEvent::User)), p1(p1), p2(p2)
|
||||
{
|
||||
}
|
||||
|
||||
App::Property* p1;
|
||||
App::Property* p2;
|
||||
};
|
||||
|
||||
class ViewProviderPythonFeatureObserver : public QObject
|
||||
{
|
||||
public:
|
||||
/// The one and only instance.
|
||||
@@ -80,13 +94,19 @@ public:
|
||||
void slotDeleteDocument(const Gui::Document&);
|
||||
|
||||
private:
|
||||
void customEvent(QEvent* e)
|
||||
{
|
||||
PropertyEvent* pe = static_cast<PropertyEvent*>(e);
|
||||
pe->p1->Paste(*pe->p2);
|
||||
delete pe->p2;
|
||||
}
|
||||
static ViewProviderPythonFeatureObserver* _singleton;
|
||||
|
||||
ViewProviderPythonFeatureObserver();
|
||||
~ViewProviderPythonFeatureObserver();
|
||||
typedef std::map<
|
||||
const App::DocumentObject*,
|
||||
std::string
|
||||
App::Property*
|
||||
> ObjectProxy;
|
||||
|
||||
std::map<const App::Document*, ObjectProxy> proxyMap;
|
||||
@@ -133,8 +153,8 @@ void ViewProviderPythonFeatureObserver::slotAppendObject(const Gui::ViewProvider
|
||||
try {
|
||||
App::Property* prop = vp.getPropertyByName("Proxy");
|
||||
if (prop && prop->isDerivedFrom(App::PropertyPythonObject::getClassTypeId())) {
|
||||
static_cast<App::PropertyPythonObject*>(prop)->fromString(jt->second);
|
||||
static_cast<App::PropertyPythonObject*>(prop)->touch();
|
||||
// make this delayed so that the corresponding item in the tree view is accessible
|
||||
QApplication::postEvent(this, new PropertyEvent(prop, jt->second));
|
||||
it->second.erase(jt);
|
||||
}
|
||||
}
|
||||
@@ -162,8 +182,7 @@ void ViewProviderPythonFeatureObserver::slotDeleteObject(const Gui::ViewProvider
|
||||
try {
|
||||
App::Property* prop = vp.getPropertyByName("Proxy");
|
||||
if (prop && prop->isDerivedFrom(App::PropertyPythonObject::getClassTypeId())) {
|
||||
std::string proxy = static_cast<App::PropertyPythonObject*>(prop)->toString();
|
||||
proxyMap[doc][docobj] = proxy;
|
||||
proxyMap[doc][docobj] = prop->Copy();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
|
||||
@@ -2781,8 +2781,11 @@ class _Shape2DView(_DraftObject):
|
||||
"The way the viewed object must be projected")
|
||||
obj.addProperty("App::PropertyIntegerList","FaceNumbers","Base",
|
||||
"The indices of the faces to be projected in Individual Faces mode")
|
||||
obj.addProperty("App::PropertyBool","HiddenLines","Base",
|
||||
"Show hidden lines")
|
||||
obj.Projection = Vector(0,0,1)
|
||||
obj.ProjectionMode = ["Solid","Individual Faces","Cutlines"]
|
||||
obj.HiddenLines = False
|
||||
_DraftObject.__init__(self,obj,"Shape2DView")
|
||||
|
||||
def execute(self,obj):
|
||||
@@ -2793,19 +2796,58 @@ class _Shape2DView(_DraftObject):
|
||||
self.createGeometry(obj)
|
||||
|
||||
def clean(self,shape):
|
||||
"returns a valid compound of edges"
|
||||
import Part
|
||||
"returns a valid compound of edges, by recreating them"
|
||||
# this is because the projection algorithm somehow creates wrong shapes.
|
||||
# they dispay fine, but on loading the file the shape is invalid
|
||||
import Part,DraftGeomUtils
|
||||
oldedges = shape.Edges
|
||||
newedges = []
|
||||
for e in oldedges:
|
||||
try:
|
||||
newedges.append(e.Curve.toShape())
|
||||
if isinstance(e.Curve,Part.Line):
|
||||
newedges.append(e.Curve.toShape())
|
||||
elif isinstance(e.Curve,Part.Circle):
|
||||
if len(e.Vertexes) > 1:
|
||||
mp = DraftGeomUtils.findMidpoint(e)
|
||||
a = Part.Arc(e.Vertexes[0].Point,mp,e.Vertexes[-1].Point).toShape()
|
||||
newedges.append(a)
|
||||
else:
|
||||
newedges.append(e.Curve.toShape())
|
||||
elif isinstance(e.Curve,Part.Ellipse):
|
||||
if len(e.Vertexes) > 1:
|
||||
a = Part.Arc(e.Curve,e.FirstParameter,e.LastParameter).toShape()
|
||||
newedges.append(a)
|
||||
else:
|
||||
newedges.append(e.Curve.toShape())
|
||||
elif isinstance(e.Curve,Part.BSplineCurve):
|
||||
if DraftGeomUtils.isLine(e.Curve):
|
||||
l = Part.Line(e.Vertexes[0].Point,e.Vertexes[-1].Point).toShape()
|
||||
newedges.append(l)
|
||||
else:
|
||||
newedges.append(e.Curve.toShape())
|
||||
else:
|
||||
newedges.append(e)
|
||||
except:
|
||||
print "Debug: error cleaning edge ",e
|
||||
return Part.makeCompound(newedges)
|
||||
|
||||
def getProjected(self,obj,shape,direction):
|
||||
"returns projected edges from a shape and a direction"
|
||||
import Part,Drawing
|
||||
edges = []
|
||||
groups = Drawing.projectEx(shape,direction)
|
||||
for g in groups[0:5]:
|
||||
if g:
|
||||
edges.append(g)
|
||||
if hasattr(obj,"HiddenLines"):
|
||||
if obj.HiddenLines:
|
||||
for g in groups[5:]:
|
||||
edges.append(g)
|
||||
#return Part.makeCompound(edges)
|
||||
return self.clean(Part.makeCompound(edges))
|
||||
|
||||
def createGeometry(self,obj):
|
||||
import Drawing, DraftGeomUtils
|
||||
import DraftGeomUtils
|
||||
pl = obj.Placement
|
||||
if obj.Base:
|
||||
if getType(obj.Base) == "SectionPlane":
|
||||
@@ -2828,9 +2870,7 @@ class _Shape2DView(_DraftObject):
|
||||
comp = Part.makeCompound(cuts)
|
||||
opl = FreeCAD.Placement(obj.Base.Placement)
|
||||
proj = opl.Rotation.multVec(FreeCAD.Vector(0,0,1))
|
||||
[visibleG0,visibleG1,hiddenG0,hiddenG1] = Drawing.project(comp,proj)
|
||||
if visibleG0:
|
||||
obj.Shape = self.clean(visibleG0)
|
||||
obj.Shape = self.getProjected(obj,comp,proj)
|
||||
elif obj.ProjectionMode == "Cutlines":
|
||||
for sh in shapes:
|
||||
if sh.Volume < 0:
|
||||
@@ -2846,9 +2886,7 @@ class _Shape2DView(_DraftObject):
|
||||
elif obj.Base.isDerivedFrom("Part::Feature"):
|
||||
if not DraftVecUtils.isNull(obj.Projection):
|
||||
if obj.ProjectionMode == "Solid":
|
||||
[visibleG0,visibleG1,hiddenG0,hiddenG1] = Drawing.project(obj.Base.Shape,obj.Projection)
|
||||
if visibleG0:
|
||||
obj.Shape = self.clean(visibleG0)
|
||||
obj.Shape = self.getProjected(obj,obj.Base.Shape,obj.Projection)
|
||||
elif obj.ProjectionMode == "Individual Faces":
|
||||
import Part
|
||||
if obj.FaceNumbers:
|
||||
@@ -2858,9 +2896,7 @@ class _Shape2DView(_DraftObject):
|
||||
faces.append(obj.Base.Shape.Faces[i])
|
||||
views = []
|
||||
for f in faces:
|
||||
[visibleG0,visibleG1,hiddenG0,hiddenG1] = Drawing.project(f,obj.Projection)
|
||||
if visibleG0:
|
||||
views.append(visibleG0)
|
||||
views.append(self.getProjected(obj,f,obj.Projection))
|
||||
if views:
|
||||
obj.Shape = Part.makeCompound(views)
|
||||
if not DraftGeomUtils.isNull(pl):
|
||||
@@ -3076,6 +3112,6 @@ class _ViewProviderClone(_ViewProviderDraftAlt):
|
||||
def getIcon(self):
|
||||
return ":/icons/Draft_Clone.svg"
|
||||
|
||||
|
||||
if not hasattr(FreeCADGui,"Snapper"):
|
||||
import DraftSnap
|
||||
if gui:
|
||||
if not hasattr(FreeCADGui,"Snapper"):
|
||||
import DraftSnap
|
||||
|
||||
@@ -642,11 +642,13 @@ class BSpline(Line):
|
||||
|
||||
def finish(self,closed=False,cont=False):
|
||||
"terminates the operation and closes the poly if asked"
|
||||
if self.ui:
|
||||
self.bsplinetrack.finalize()
|
||||
if not Draft.getParam("UiMode"):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
if (len(self.node) > 1):
|
||||
old = self.obj.Name
|
||||
self.doc.removeObject(old)
|
||||
todo.delay(self.doc.removeObject,old)
|
||||
try:
|
||||
# building command string
|
||||
rot,sup,pts,fil = self.getStrings()
|
||||
@@ -656,8 +658,6 @@ class BSpline(Line):
|
||||
'Draft.makeBSpline(points,closed='+str(closed)+',face='+fil+',support='+sup+')'])
|
||||
except:
|
||||
print "Draft: error delaying commit"
|
||||
if self.ui:
|
||||
self.bsplinetrack.finalize()
|
||||
Creator.finish(self)
|
||||
if self.ui:
|
||||
if self.ui.continueMode:
|
||||
@@ -1381,6 +1381,7 @@ class Dimension(Creator):
|
||||
self.arcmode = False
|
||||
self.point2 = None
|
||||
self.force = None
|
||||
self.info = None
|
||||
msg(translate("draft", "Pick first point:\n"))
|
||||
FreeCADGui.draftToolBar.show()
|
||||
|
||||
@@ -1451,9 +1452,9 @@ class Dimension(Creator):
|
||||
self.finish()
|
||||
elif arg["Type"] == "SoLocation2Event": #mouse movement detection
|
||||
shift = hasMod(arg,MODCONSTRAIN)
|
||||
self.point,ctrlPoint,self.info = getPoint(self,arg)
|
||||
if self.arcmode or self.point2:
|
||||
setMod(arg,MODCONSTRAIN,False)
|
||||
self.point,ctrlPoint,info = getPoint(self,arg)
|
||||
if hasMod(arg,MODALT) and (len(self.node)<3):
|
||||
self.dimtrack.off()
|
||||
if not self.altdown:
|
||||
@@ -1534,11 +1535,11 @@ class Dimension(Creator):
|
||||
if (not self.node) and (not self.support):
|
||||
self.support = getSupport(arg)
|
||||
if hasMod(arg,MODALT) and (len(self.node)<3):
|
||||
print "snapped: ",info
|
||||
if info:
|
||||
ob = self.doc.getObject(info['Object'])
|
||||
if 'Edge' in info['Component']:
|
||||
num = int(info['Component'].lstrip('Edge'))-1
|
||||
print "snapped: ",self.info
|
||||
if self.info:
|
||||
ob = self.doc.getObject(self.info['Object'])
|
||||
if 'Edge' in self.info['Component']:
|
||||
num = int(self.info['Component'].lstrip('Edge'))-1
|
||||
ed = ob.Shape.Edges[num]
|
||||
v1 = ed.Vertexes[0].Point
|
||||
v2 = ed.Vertexes[-1].Point
|
||||
|
||||
@@ -235,7 +235,9 @@ class dimTracker(Tracker):
|
||||
self.p1 = self.p2 = self.p3 = None
|
||||
|
||||
def update(self,pts):
|
||||
if len(pts) == 1:
|
||||
if not pts:
|
||||
return
|
||||
elif len(pts) == 1:
|
||||
self.p3 = pts[0]
|
||||
else:
|
||||
self.p1 = pts[0]
|
||||
|
||||
@@ -198,6 +198,7 @@ void PartExport initPart()
|
||||
Part::RuledSurface ::init();
|
||||
Part::Loft ::init();
|
||||
Part::Sweep ::init();
|
||||
Part::Offset ::init();
|
||||
|
||||
// Geometry types
|
||||
Part::Geometry ::init();
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
# include <BRepBuilderAPI_MakeWire.hxx>
|
||||
# include <BRepOffsetAPI_MakePipeShell.hxx>
|
||||
# include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
# include <Precision.hxx>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -334,3 +335,67 @@ App::DocumentObjectExecReturn *Sweep::execute(void)
|
||||
return new App::DocumentObjectExecReturn(e->GetMessageString());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const char* Part::Offset::ModeEnums[]= {"Skin","Pipe", "RectoVerso",NULL};
|
||||
const char* Part::Offset::JoinEnums[]= {"Arc","Tangent", "Intersection",NULL};
|
||||
|
||||
PROPERTY_SOURCE(Part::Offset, Part::Feature)
|
||||
|
||||
Offset::Offset()
|
||||
{
|
||||
ADD_PROPERTY_TYPE(Source,(0),"Offset",App::Prop_None,"Source shape");
|
||||
ADD_PROPERTY_TYPE(Value,(1.0),"Offset",App::Prop_None,"Offset value");
|
||||
ADD_PROPERTY_TYPE(Mode,(long(0)),"Offset",App::Prop_None,"Mode");
|
||||
Mode.setEnums(ModeEnums);
|
||||
ADD_PROPERTY_TYPE(Join,(long(0)),"Offset",App::Prop_None,"Join type");
|
||||
Join.setEnums(JoinEnums);
|
||||
ADD_PROPERTY_TYPE(Intersection,(false),"Offset",App::Prop_None,"Intersection");
|
||||
ADD_PROPERTY_TYPE(SelfIntersection,(false),"Offset",App::Prop_None,"Self Intersection");
|
||||
ADD_PROPERTY_TYPE(Fill,(false),"Offset",App::Prop_None,"Fill offset");
|
||||
}
|
||||
|
||||
short Offset::mustExecute() const
|
||||
{
|
||||
if (Source.isTouched())
|
||||
return 1;
|
||||
if (Value.isTouched())
|
||||
return 1;
|
||||
if (Mode.isTouched())
|
||||
return 1;
|
||||
if (Join.isTouched())
|
||||
return 1;
|
||||
if (Intersection.isTouched())
|
||||
return 1;
|
||||
if (SelfIntersection.isTouched())
|
||||
return 1;
|
||||
if (Fill.isTouched())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Offset::onChanged(const App::Property* prop)
|
||||
{
|
||||
Part::Feature::onChanged(prop);
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *Offset::execute(void)
|
||||
{
|
||||
App::DocumentObject* source = Source.getValue();
|
||||
if (!(source && source->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())))
|
||||
return new App::DocumentObjectExecReturn("No source shape linked.");
|
||||
double offset = Value.getValue();
|
||||
double tol = Precision::Confusion();
|
||||
bool inter = Intersection.getValue();
|
||||
bool self = SelfIntersection.getValue();
|
||||
short mode = (short)Mode.getValue();
|
||||
short join = (short)Join.getValue();
|
||||
bool fill = Fill.getValue();
|
||||
const TopoShape& shape = static_cast<Part::Feature*>(source)->Shape.getShape();
|
||||
if (fabs(offset) > 2*tol)
|
||||
this->Shape.setValue(shape.makeOffsetShape(offset, tol, inter, self, mode, join, fill));
|
||||
else
|
||||
this->Shape.setValue(shape);
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define PART_FEATURES_H
|
||||
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
|
||||
namespace Part
|
||||
@@ -106,6 +107,39 @@ private:
|
||||
static const char* TransitionEnums[];
|
||||
};
|
||||
|
||||
class Offset : public Part::Feature
|
||||
{
|
||||
PROPERTY_HEADER(Part::Offset);
|
||||
|
||||
public:
|
||||
Offset();
|
||||
|
||||
App::PropertyLink Source;
|
||||
App::PropertyFloat Value;
|
||||
App::PropertyEnumeration Mode;
|
||||
App::PropertyEnumeration Join;
|
||||
App::PropertyBool Intersection;
|
||||
App::PropertyBool SelfIntersection;
|
||||
App::PropertyBool Fill;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
/// recalculate the feature
|
||||
App::DocumentObjectExecReturn *execute(void);
|
||||
short mustExecute() const;
|
||||
const char* getViewProviderName(void) const {
|
||||
return "PartGui::ViewProviderOffset";
|
||||
}
|
||||
//@}
|
||||
|
||||
protected:
|
||||
void onChanged (const App::Property* prop);
|
||||
|
||||
private:
|
||||
static const char* ModeEnums[];
|
||||
static const char* JoinEnums[];
|
||||
};
|
||||
|
||||
} //namespace Part
|
||||
|
||||
|
||||
|
||||
@@ -158,20 +158,28 @@ PyObject *PropertyPartShape::getPyObject(void)
|
||||
{
|
||||
case TopAbs_COMPOUND:
|
||||
prop = new TopoShapeCompoundPy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_COMPSOLID:
|
||||
prop = new TopoShapeCompSolidPy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_SOLID:
|
||||
prop = new TopoShapeSolidPy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_SHELL:
|
||||
prop = new TopoShapeShellPy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_FACE:
|
||||
prop = new TopoShapeFacePy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_WIRE:
|
||||
prop = new TopoShapeWirePy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_EDGE:
|
||||
prop = new TopoShapeEdgePy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_VERTEX:
|
||||
prop = new TopoShapeVertexPy(new TopoShape(sh));
|
||||
break;
|
||||
case TopAbs_SHAPE:
|
||||
default:
|
||||
prop = new TopoShapePy(new TopoShape(sh));
|
||||
|
||||
@@ -1690,21 +1690,107 @@ TopoDS_Shape TopoShape::revolve(const gp_Ax1& axis, double d) const
|
||||
return mkRevol.Shape();
|
||||
}
|
||||
|
||||
TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace,
|
||||
Standard_Real offset, Standard_Real tolerance) const
|
||||
{
|
||||
BRepOffsetAPI_MakeThickSolid mkThick(this->_Shape, remFace, offset, tolerance);
|
||||
return mkThick.Shape();
|
||||
}
|
||||
//#include <BRepFill.hxx>
|
||||
//#include <BRepTools_Quilt.hxx>
|
||||
|
||||
TopoDS_Shape TopoShape::makeOffset(double offset, double tol, bool intersection,
|
||||
bool selfInter, short offsetMode, short join)
|
||||
TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersection,
|
||||
bool selfInter, short offsetMode, short join,
|
||||
bool fill) const
|
||||
{
|
||||
BRepOffsetAPI_MakeOffsetShape mkOffset(this->_Shape, offset, tol, BRepOffset_Mode(offsetMode),
|
||||
intersection ? Standard_True : Standard_False,
|
||||
selfInter ? Standard_True : Standard_False,
|
||||
GeomAbs_JoinType(join));
|
||||
return mkOffset.Shape();
|
||||
const TopoDS_Shape& res = mkOffset.Shape();
|
||||
if (!fill)
|
||||
return res;
|
||||
#if 1
|
||||
//s=Part.makePlane(10,10)
|
||||
//s.makeOffsetShape(1.0,0.01,False,False,0,0,True)
|
||||
const BRepOffset_MakeOffset& off = mkOffset.MakeOffset();
|
||||
const BRepAlgo_Image& img = off.OffsetEdgesFromShapes();
|
||||
|
||||
// build up map edge->face
|
||||
TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
|
||||
TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
|
||||
TopTools_IndexedMapOfShape mapOfShape;
|
||||
TopExp::MapShapes(this->_Shape, TopAbs_EDGE, mapOfShape);
|
||||
|
||||
TopoDS_Shell shell;
|
||||
BRep_Builder builder;
|
||||
TopExp_Explorer xp;
|
||||
builder.MakeShell(shell);
|
||||
|
||||
for (xp.Init(this->_Shape,TopAbs_FACE); xp.More(); xp.Next()) {
|
||||
builder.Add(shell, xp.Current());
|
||||
}
|
||||
|
||||
for (int i=1; i<= edge2Face.Extent(); ++i) {
|
||||
const TopTools_ListOfShape& los = edge2Face.FindFromIndex(i);
|
||||
if (los.Extent() == 1) {
|
||||
// set the index value as user data to use it in accept()
|
||||
const TopoDS_Shape& edge = edge2Face.FindKey(i);
|
||||
Standard_Boolean ok = img.HasImage(edge);
|
||||
if (ok) {
|
||||
const TopTools_ListOfShape& edges = img.Image(edge);
|
||||
TopTools_ListIteratorOfListOfShape it;
|
||||
it.Initialize(edges);
|
||||
BRepOffsetAPI_ThruSections aGenerator (0,0);
|
||||
aGenerator.AddWire(BRepBuilderAPI_MakeWire(TopoDS::Edge(edge)).Wire());
|
||||
aGenerator.AddWire(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Value())).Wire());
|
||||
aGenerator.Build();
|
||||
for (xp.Init(aGenerator.Shape(),TopAbs_FACE); xp.More(); xp.Next()) {
|
||||
builder.Add(shell, xp.Current());
|
||||
}
|
||||
//TopoDS_Face face = BRepFill::Face(TopoDS::Edge(edge), TopoDS::Edge(it.Value()));
|
||||
//builder.Add(shell, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (xp.Init(mkOffset.Shape(),TopAbs_FACE); xp.More(); xp.Next()) {
|
||||
builder.Add(shell, xp.Current());
|
||||
}
|
||||
|
||||
//BRepBuilderAPI_Sewing sew(offset);
|
||||
//sew.Load(this->_Shape);
|
||||
//sew.Add(mkOffset.Shape());
|
||||
//sew.Perform();
|
||||
|
||||
//shell.Closed(Standard_True);
|
||||
|
||||
return shell;
|
||||
#else
|
||||
TopoDS_Solid Res;
|
||||
TopExp_Explorer exp;
|
||||
BRep_Builder B;
|
||||
B.MakeSolid(Res);
|
||||
|
||||
BRepTools_Quilt Glue;
|
||||
for (exp.Init(this->_Shape,TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
Glue.Add (exp.Current());
|
||||
}
|
||||
for (exp.Init(mkOffset.Shape(),TopAbs_FACE);exp.More(); exp.Next()) {
|
||||
Glue.Add (exp.Current().Reversed());
|
||||
}
|
||||
TopoDS_Shape S = Glue.Shells();
|
||||
for (exp.Init(S,TopAbs_SHELL); exp.More(); exp.Next()) {
|
||||
B.Add(Res,exp.Current());
|
||||
}
|
||||
Res.Closed(Standard_True);
|
||||
return Res;
|
||||
#endif
|
||||
}
|
||||
|
||||
TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace,
|
||||
double offset, double tol, bool intersection,
|
||||
bool selfInter, short offsetMode, short join) const
|
||||
{
|
||||
BRepOffsetAPI_MakeThickSolid mkThick(this->_Shape, remFace, offset, tol, BRepOffset_Mode(offsetMode),
|
||||
intersection ? Standard_True : Standard_False,
|
||||
selfInter ? Standard_True : Standard_False,
|
||||
GeomAbs_JoinType(join));
|
||||
return mkThick.Shape();
|
||||
}
|
||||
|
||||
void TopoShape::transformGeometry(const Base::Matrix4D &rclMat)
|
||||
|
||||
@@ -158,8 +158,6 @@ public:
|
||||
const Standard_Boolean isFrenet = Standard_False) const;
|
||||
TopoDS_Shape makePrism(const gp_Vec&) const;
|
||||
TopoDS_Shape revolve(const gp_Ax1&, double d) const;
|
||||
TopoDS_Shape makeThickSolid(const TopTools_ListOfShape& remFace,
|
||||
Standard_Real offset, Standard_Real tolerance) const;
|
||||
TopoDS_Shape makeSweep(const TopoDS_Shape& profile, double, int) const;
|
||||
TopoDS_Shape makeTube(double radius, double tol, int cont, int maxdeg, int maxsegm) const;
|
||||
TopoDS_Shape makeHelix(Standard_Real pitch, Standard_Real height,
|
||||
@@ -168,9 +166,13 @@ public:
|
||||
Standard_Real height, Standard_Real radius) const;
|
||||
TopoDS_Shape makeLoft(const TopTools_ListOfShape& profiles, Standard_Boolean isSolid,
|
||||
Standard_Boolean isRuled) const;
|
||||
TopoDS_Shape makeOffset(double offset, double tol,
|
||||
TopoDS_Shape makeOffsetShape(double offset, double tol,
|
||||
bool intersection = false, bool selfInter = false,
|
||||
short offsetMode = 0, short join = 0);
|
||||
short offsetMode = 0, short join = 0, bool fill = false) const;
|
||||
TopoDS_Shape makeThickSolid(const TopTools_ListOfShape& remFace,
|
||||
double offset, double tol,
|
||||
bool intersection = false, bool selfInter = false,
|
||||
short offsetMode = 0, short join = 0) const;
|
||||
//@}
|
||||
|
||||
/** @name Manipulation*/
|
||||
|
||||
@@ -189,7 +189,7 @@ the underlying geometry.</UserDocu>
|
||||
</Methode>
|
||||
<Methode Name="makeThickness" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>makeThickness(List of shapes, Ofset (Float), Tolerance (Float)) -> Shape
|
||||
<UserDocu>makeThickness(List of shapes, Offset (Float), Tolerance (Float)) -> Shape
|
||||
A hollowed solid is built from an initial solid and a set of faces on this solid,
|
||||
which are to be removed. The remaining faces of the solid become the walls of
|
||||
the hollowed solid, their thickness defined at the time of construction.</UserDocu>
|
||||
|
||||
@@ -1024,7 +1024,15 @@ PyObject* TopoShapePy::makeThickness(PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
double offset, tolerance;
|
||||
if (!PyArg_ParseTuple(args, "O!dd", &(PyList_Type), &obj, &offset, &tolerance))
|
||||
PyObject* inter = Py_False;
|
||||
PyObject* self_inter = Py_False;
|
||||
short offsetMode = 0, join = 0;
|
||||
if (!PyArg_ParseTuple(args, "O!dd|O!O!hh",
|
||||
&(PyList_Type), &obj,
|
||||
&offset, &tolerance,
|
||||
&(PyBool_Type), &inter,
|
||||
&(PyBool_Type), &self_inter,
|
||||
&offsetMode, &join))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
@@ -1037,7 +1045,8 @@ PyObject* TopoShapePy::makeThickness(PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
TopoDS_Shape shape = this->getTopoShapePtr()->makeThickSolid(facesToRemove, offset, tolerance);
|
||||
TopoDS_Shape shape = this->getTopoShapePtr()->makeThickSolid(facesToRemove, offset, tolerance,
|
||||
(inter == Py_True), (self_inter == Py_True), offsetMode, join);
|
||||
return new TopoShapeSolidPy(new TopoShape(shape));
|
||||
}
|
||||
catch (Standard_Failure) {
|
||||
@@ -1052,17 +1061,19 @@ PyObject* TopoShapePy::makeOffsetShape(PyObject *args)
|
||||
double offset, tolerance;
|
||||
PyObject* inter = Py_False;
|
||||
PyObject* self_inter = Py_False;
|
||||
PyObject* fill = Py_False;
|
||||
short offsetMode = 0, join = 0;
|
||||
if (!PyArg_ParseTuple(args, "dd|O!O!hh",
|
||||
if (!PyArg_ParseTuple(args, "dd|O!O!hhO!",
|
||||
&offset, &tolerance,
|
||||
&(PyBool_Type), &inter,
|
||||
&(PyBool_Type), &self_inter,
|
||||
&offsetMode, &join))
|
||||
&offsetMode, &join,
|
||||
&(PyBool_Type), &fill))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
TopoDS_Shape shape = this->getTopoShapePtr()->makeOffset(offset, tolerance,
|
||||
(inter == Py_True), (self_inter == Py_True), offsetMode, join);
|
||||
TopoDS_Shape shape = this->getTopoShapePtr()->makeOffsetShape(offset, tolerance,
|
||||
(inter == Py_True), (self_inter == Py_True), offsetMode, join, (fill == Py_True));
|
||||
return new TopoShapePy(new TopoShape(shape));
|
||||
}
|
||||
catch (Standard_Failure) {
|
||||
|
||||
@@ -105,6 +105,7 @@ void PartGuiExport initPartGui()
|
||||
PartGui::ViewProviderRevolution ::init();
|
||||
PartGui::ViewProviderLoft ::init();
|
||||
PartGui::ViewProviderSweep ::init();
|
||||
PartGui::ViewProviderOffset ::init();
|
||||
PartGui::ViewProviderCustom ::init();
|
||||
PartGui::ViewProviderCustomPython ::init();
|
||||
PartGui::ViewProviderBoolean ::init();
|
||||
|
||||
@@ -41,6 +41,7 @@ set(PartGui_MOC_HDRS
|
||||
TaskFaceColors.h
|
||||
TaskShapeBuilder.h
|
||||
TaskLoft.h
|
||||
TaskOffset.h
|
||||
TaskSweep.h
|
||||
TaskCheckGeometry.h
|
||||
)
|
||||
@@ -67,6 +68,7 @@ set(PartGui_UIC_SRCS
|
||||
TaskFaceColors.ui
|
||||
TaskShapeBuilder.ui
|
||||
TaskLoft.ui
|
||||
TaskOffset.ui
|
||||
TaskSweep.ui
|
||||
)
|
||||
qt4_wrap_ui(PartGui_UIC_HDRS ${PartGui_UIC_SRCS})
|
||||
@@ -158,6 +160,9 @@ SET(PartGui_SRCS
|
||||
TaskLoft.cpp
|
||||
TaskLoft.h
|
||||
TaskLoft.ui
|
||||
TaskOffset.cpp
|
||||
TaskOffset.h
|
||||
TaskOffset.ui
|
||||
TaskSweep.cpp
|
||||
TaskSweep.h
|
||||
TaskSweep.ui
|
||||
|
||||
@@ -990,6 +990,51 @@ bool CmdPartSweep::isActive(void)
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
DEF_STD_CMD_A(CmdPartOffset);
|
||||
|
||||
CmdPartOffset::CmdPartOffset()
|
||||
: Command("Part_Offset")
|
||||
{
|
||||
sAppModule = "Part";
|
||||
sGroup = QT_TR_NOOP("Part");
|
||||
sMenuText = QT_TR_NOOP("Offset...");
|
||||
sToolTipText = QT_TR_NOOP("Utility to offset");
|
||||
sWhatsThis = sToolTipText;
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Part_Offset";
|
||||
}
|
||||
|
||||
void CmdPartOffset::activated(int iMsg)
|
||||
{
|
||||
App::DocumentObject* shape = getSelection().getObjectsOfType(Part::Feature::getClassTypeId()).front();
|
||||
std::string offset = getUniqueObjectName("Offset");
|
||||
|
||||
openCommand("Make Offset");
|
||||
doCommand(Doc,"App.ActiveDocument.addObject(\"Part::Offset\",\"%s\")",offset.c_str());
|
||||
doCommand(Doc,"App.ActiveDocument.%s.Source = App.ActiveDocument.%s" ,offset.c_str(), shape->getNameInDocument());
|
||||
doCommand(Doc,"App.ActiveDocument.%s.Value = 1.0",offset.c_str());
|
||||
updateActive();
|
||||
//if (isActiveObjectValid())
|
||||
// doCommand(Gui,"Gui.ActiveDocument.hide(\"%s\")",shape->getNameInDocument());
|
||||
doCommand(Gui,"Gui.ActiveDocument.setEdit('%s')",offset.c_str());
|
||||
|
||||
//commitCommand();
|
||||
adjustCameraPosition();
|
||||
|
||||
copyVisual(offset.c_str(), "ShapeColor", shape->getNameInDocument());
|
||||
copyVisual(offset.c_str(), "LineColor" , shape->getNameInDocument());
|
||||
copyVisual(offset.c_str(), "PointColor", shape->getNameInDocument());
|
||||
}
|
||||
|
||||
bool CmdPartOffset::isActive(void)
|
||||
{
|
||||
Base::Type partid = Base::Type::fromName("Part::Feature");
|
||||
bool objectsSelected = Gui::Selection().countObjectsOfType(partid) == 1;
|
||||
return (objectsSelected && !Gui::Control().activeDialog());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
DEF_STD_CMD_A(CmdShapeInfo);
|
||||
|
||||
CmdShapeInfo::CmdShapeInfo()
|
||||
@@ -1260,5 +1305,6 @@ void CreatePartCommands(void)
|
||||
rcCmdMgr.addCommand(new CmdPartBuilder());
|
||||
rcCmdMgr.addCommand(new CmdPartLoft());
|
||||
rcCmdMgr.addCommand(new CmdPartSweep());
|
||||
rcCmdMgr.addCommand(new CmdPartOffset());
|
||||
rcCmdMgr.addCommand(new CmdCheckGeometry());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<file>icons/Part_Loft.svg</file>
|
||||
<file>icons/Part_Mirror.svg</file>
|
||||
<file>icons/Part_MirrorPNG.png</file>
|
||||
<file>icons/Part_Offset.svg</file>
|
||||
<file>icons/Part_Revolve.svg</file>
|
||||
<file>icons/Part_RuledSurface.svg</file>
|
||||
<file>icons/Part_Section.svg</file>
|
||||
|
||||
185
src/Mod/Part/Gui/Resources/icons/Part_Offset.svg
Normal file
185
src/Mod/Part/Gui/Resources/icons/Part_Offset.svg
Normal file
@@ -0,0 +1,185 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2682"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="Part_Offset.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs2684">
|
||||
<linearGradient
|
||||
id="linearGradient3593">
|
||||
<stop
|
||||
style="stop-color:#c8e0f9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3595" />
|
||||
<stop
|
||||
style="stop-color:#637dca;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3597" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428"
|
||||
gradientTransform="matrix(0.9327663,0,0,0.9327663,-298.15651,8.1913381)" />
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2690" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient2401"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.959337,0.05179994,0,0.7352325,-29.610908,-1.231413)"
|
||||
cx="51.105499"
|
||||
cy="23.807407"
|
||||
fx="51.105499"
|
||||
fy="23.807407"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient2404"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.2993671,-1.5757258e-2,8.4161044e-3,0.9850979,-94.354208,-10.998387)"
|
||||
cx="48.288067"
|
||||
cy="46.74614"
|
||||
fx="48.288067"
|
||||
fy="46.74614"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3377"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9327663,0,0,0.9327663,-267.16323,12.515981)"
|
||||
cx="317.68173"
|
||||
cy="35.227276"
|
||||
fx="317.68173"
|
||||
fy="35.227276"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864-7"
|
||||
id="radialGradient2404-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.2993671,-0.01575726,0.0084161,0.9850979,-94.354208,-10.998387)"
|
||||
cx="48.288067"
|
||||
cy="46.74614"
|
||||
fx="48.288067"
|
||||
fy="46.74614"
|
||||
r="19.571428" />
|
||||
<linearGradient
|
||||
id="linearGradient3864-7">
|
||||
<stop
|
||||
id="stop3866-4"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3868-0"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="46.74614"
|
||||
fx="48.288067"
|
||||
cy="46.74614"
|
||||
cx="48.288067"
|
||||
gradientTransform="matrix(2.2993671,-0.01575726,0.0084161,0.9850979,-94.867845,6.893181)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3014"
|
||||
xlink:href="#linearGradient3864-7"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.75"
|
||||
inkscape:cx="61.342448"
|
||||
inkscape:cy="37.42735"
|
||||
inkscape:current-layer="g3381"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="758"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="19"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata2687">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g3381">
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="rect3633"
|
||||
d="M 23.530174,28.496274 L 55.6968,30.8755 L 36.406189,40.084227 L 4.2395647,37.705001 L 23.530174,28.496274 z"
|
||||
style="fill:url(#radialGradient2404);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.2;stroke-linecap:butt;stroke-linejoin:bevel;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
id="rect3638"
|
||||
d="m 28.577651,5.423645 -15.10716,13.814131 10.450664,0.564291 0,14.041034 10.794612,0.58286 0,-14.041035 8.969043,0.484289 -15.107159,-15.44557 z"
|
||||
style="fill:url(#radialGradient2401);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="rect3633-9"
|
||||
d="M 23.016537,46.387842 55.183163,48.767068 35.892552,57.975795 3.725928,55.596569 23.016537,46.387842 z"
|
||||
style="fill:url(#radialGradient3014);fill-opacity:1;fill-rule:evenodd;stroke:#000137;stroke-width:2.20000005;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
232
src/Mod/Part/Gui/TaskOffset.cpp
Normal file
232
src/Mod/Part/Gui/TaskOffset.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2012 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* 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 this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <QMessageBox>
|
||||
# include <QTextStream>
|
||||
#endif
|
||||
|
||||
#include "ui_TaskOffset.h"
|
||||
#include "TaskOffset.h"
|
||||
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <Gui/SelectionFilter.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Mod/Part/App/PartFeatures.h>
|
||||
|
||||
|
||||
using namespace PartGui;
|
||||
|
||||
class OffsetWidget::Private
|
||||
{
|
||||
public:
|
||||
Ui_TaskOffset ui;
|
||||
Part::Offset* offset;
|
||||
Private()
|
||||
{
|
||||
}
|
||||
~Private()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* TRANSLATOR PartGui::OffsetWidget */
|
||||
|
||||
OffsetWidget::OffsetWidget(Part::Offset* offset, QWidget* parent)
|
||||
: d(new Private())
|
||||
{
|
||||
Gui::Application::Instance->runPythonCode("from FreeCAD import Base");
|
||||
Gui::Application::Instance->runPythonCode("import Part");
|
||||
|
||||
d->offset = offset;
|
||||
d->ui.setupUi(this);
|
||||
d->ui.spinOffset->setValue(d->offset->Value.getValue());
|
||||
d->ui.spinOffset->setRange(-INT_MAX, INT_MAX);
|
||||
d->ui.spinOffset->setSingleStep(0.1);
|
||||
}
|
||||
|
||||
OffsetWidget::~OffsetWidget()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Part::Offset* OffsetWidget::getObject() const
|
||||
{
|
||||
return d->offset;
|
||||
}
|
||||
|
||||
void OffsetWidget::on_spinOffset_valueChanged(double val)
|
||||
{
|
||||
d->offset->Value.setValue((float)val);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_modeType_activated(int val)
|
||||
{
|
||||
d->offset->Mode.setValue(val);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_joinType_activated(int val)
|
||||
{
|
||||
d->offset->Join.setValue((float)val);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_intersection_toggled(bool on)
|
||||
{
|
||||
d->offset->Intersection.setValue(on);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_selfIntersection_toggled(bool on)
|
||||
{
|
||||
d->offset->SelfIntersection.setValue(on);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_fillOffset_toggled(bool on)
|
||||
{
|
||||
d->offset->Fill.setValue(on);
|
||||
if (d->ui.updateView->isChecked())
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
|
||||
void OffsetWidget::on_updateView_toggled(bool on)
|
||||
{
|
||||
if (on) {
|
||||
d->offset->getDocument()->recomputeFeature(d->offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool OffsetWidget::accept()
|
||||
{
|
||||
std::string name = d->offset->getNameInDocument();
|
||||
|
||||
try {
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Value = %f",
|
||||
name.c_str(),d->ui.spinOffset->value());
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Mode = %i",
|
||||
name.c_str(),d->ui.modeType->currentIndex());
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Join = %i",
|
||||
name.c_str(),d->ui.joinType->currentIndex());
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Intersection = %s",
|
||||
name.c_str(),d->ui.intersection->isChecked() ? "True" : "False");
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.SelfIntersection = %s",
|
||||
name.c_str(),d->ui.selfIntersection->isChecked() ? "True" : "False");
|
||||
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()");
|
||||
if (!d->offset->isValid())
|
||||
throw Base::Exception(d->offset->getStatusString());
|
||||
Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.resetEdit()");
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
QMessageBox::warning(this, tr("Input error"), QString::fromAscii(e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OffsetWidget::reject()
|
||||
{
|
||||
// get the support and Sketch
|
||||
App::DocumentObject* source = d->offset->Source.getValue();
|
||||
if (source){
|
||||
Gui::Application::Instance->getViewProvider(source)->show();
|
||||
}
|
||||
|
||||
// roll back the done things
|
||||
Gui::Command::abortCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.resetEdit()");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OffsetWidget::changeEvent(QEvent *e)
|
||||
{
|
||||
QWidget::changeEvent(e);
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
d->ui.retranslateUi(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* TRANSLATOR PartGui::TaskOffset */
|
||||
|
||||
TaskOffset::TaskOffset(Part::Offset* offset)
|
||||
{
|
||||
widget = new OffsetWidget(offset);
|
||||
taskbox = new Gui::TaskView::TaskBox(
|
||||
Gui::BitmapFactory().pixmap("Part_Offset"),
|
||||
widget->windowTitle(), true, 0);
|
||||
taskbox->groupLayout()->addWidget(widget);
|
||||
Content.push_back(taskbox);
|
||||
}
|
||||
|
||||
TaskOffset::~TaskOffset()
|
||||
{
|
||||
}
|
||||
|
||||
Part::Offset* TaskOffset::getObject() const
|
||||
{
|
||||
return widget->getObject();
|
||||
}
|
||||
|
||||
void TaskOffset::open()
|
||||
{
|
||||
}
|
||||
|
||||
void TaskOffset::clicked(int)
|
||||
{
|
||||
}
|
||||
|
||||
bool TaskOffset::accept()
|
||||
{
|
||||
return widget->accept();
|
||||
}
|
||||
|
||||
bool TaskOffset::reject()
|
||||
{
|
||||
return widget->reject();
|
||||
}
|
||||
|
||||
#include "moc_TaskOffset.cpp"
|
||||
87
src/Mod/Part/Gui/TaskOffset.h
Normal file
87
src/Mod/Part/Gui/TaskOffset.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2012 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* 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 this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef PARTGUI_TASKOFFSET_H
|
||||
#define PARTGUI_TASKOFFSET_H
|
||||
|
||||
#include <Gui/TaskView/TaskView.h>
|
||||
#include <Gui/TaskView/TaskDialog.h>
|
||||
|
||||
namespace Part { class Offset; }
|
||||
namespace PartGui {
|
||||
|
||||
class OffsetWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OffsetWidget(Part::Offset*, QWidget* parent = 0);
|
||||
~OffsetWidget();
|
||||
|
||||
bool accept();
|
||||
bool reject();
|
||||
Part::Offset* getObject() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_spinOffset_valueChanged(double);
|
||||
void on_modeType_activated(int);
|
||||
void on_joinType_activated(int);
|
||||
void on_intersection_toggled(bool);
|
||||
void on_selfIntersection_toggled(bool);
|
||||
void on_fillOffset_toggled(bool);
|
||||
void on_updateView_toggled(bool);
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent *e);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private* d;
|
||||
};
|
||||
|
||||
class TaskOffset : public Gui::TaskView::TaskDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskOffset(Part::Offset*);
|
||||
~TaskOffset();
|
||||
|
||||
public:
|
||||
void open();
|
||||
bool accept();
|
||||
bool reject();
|
||||
void clicked(int);
|
||||
Part::Offset* getObject() const;
|
||||
|
||||
QDialogButtonBox::StandardButtons getStandardButtons() const
|
||||
{ return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; }
|
||||
|
||||
private:
|
||||
OffsetWidget* widget;
|
||||
Gui::TaskView::TaskBox* taskbox;
|
||||
};
|
||||
|
||||
} //namespace PartGui
|
||||
|
||||
#endif // PARTGUI_TASKOFFSET_H
|
||||
128
src/Mod/Part/Gui/TaskOffset.ui
Normal file
128
src/Mod/Part/Gui/TaskOffset.ui
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PartGui::TaskOffset</class>
|
||||
<widget class="QWidget" name="PartGui::TaskOffset">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>256</width>
|
||||
<height>260</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelOffset">
|
||||
<property name="text">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinOffset"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="modeType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Skin</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pipe</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>RectoVerso</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Join type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="joinType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Arc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tangent</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Intersection</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="intersection">
|
||||
<property name="text">
|
||||
<string>Intersection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="selfIntersection">
|
||||
<property name="text">
|
||||
<string>Self-intersection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="updateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="fillOffset">
|
||||
<property name="text">
|
||||
<string>Fill offset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>spinOffset</tabstop>
|
||||
<tabstop>modeType</tabstop>
|
||||
<tabstop>joinType</tabstop>
|
||||
<tabstop>intersection</tabstop>
|
||||
<tabstop>selfIntersection</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <Gui/Document.h>
|
||||
#include "ViewProviderMirror.h"
|
||||
#include "DlgFilletEdges.h"
|
||||
#include "TaskOffset.h"
|
||||
|
||||
using namespace PartGui;
|
||||
|
||||
@@ -395,3 +396,84 @@ bool ViewProviderSweep::onDelete(const std::vector<std::string> &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
PROPERTY_SOURCE(PartGui::ViewProviderOffset, PartGui::ViewProviderPart)
|
||||
|
||||
ViewProviderOffset::ViewProviderOffset()
|
||||
{
|
||||
sPixmap = "Part_Offset";
|
||||
}
|
||||
|
||||
ViewProviderOffset::~ViewProviderOffset()
|
||||
{
|
||||
}
|
||||
|
||||
void ViewProviderOffset::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
|
||||
{
|
||||
QAction* act;
|
||||
act = menu->addAction(QObject::tr("Edit offset"), receiver, member);
|
||||
act->setData(QVariant((int)ViewProvider::Default));
|
||||
PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member);
|
||||
}
|
||||
|
||||
bool ViewProviderOffset::setEdit(int ModNum)
|
||||
{
|
||||
if (ModNum == ViewProvider::Default ) {
|
||||
Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog();
|
||||
TaskOffset* offsetDlg = qobject_cast<TaskOffset*>(dlg);
|
||||
if (offsetDlg && offsetDlg->getObject() != this->getObject())
|
||||
offsetDlg = 0; // another pad left open its task panel
|
||||
if (dlg && !offsetDlg) {
|
||||
if (dlg->canClose())
|
||||
Gui::Control().closeDialog();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear the selection (convenience)
|
||||
Gui::Selection().clearSelection();
|
||||
|
||||
// start the edit dialog
|
||||
if (offsetDlg)
|
||||
Gui::Control().showDialog(offsetDlg);
|
||||
else
|
||||
Gui::Control().showDialog(new TaskOffset(static_cast<Part::Offset*>(getObject())));
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return ViewProviderPart::setEdit(ModNum);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProviderOffset::unsetEdit(int ModNum)
|
||||
{
|
||||
if (ModNum == ViewProvider::Default) {
|
||||
// when pressing ESC make sure to close the dialog
|
||||
Gui::Control().closeDialog();
|
||||
}
|
||||
else {
|
||||
PartGui::ViewProviderPart::unsetEdit(ModNum);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> ViewProviderOffset::claimChildren() const
|
||||
{
|
||||
std::vector<App::DocumentObject*> child;
|
||||
child.push_back(static_cast<Part::Offset*>(getObject())->Source.getValue());
|
||||
return child;
|
||||
}
|
||||
|
||||
bool ViewProviderOffset::onDelete(const std::vector<std::string> &)
|
||||
{
|
||||
// get the support and Sketch
|
||||
Part::Offset* offset = static_cast<Part::Offset*>(getObject());
|
||||
App::DocumentObject* source = offset->Source.getValue();
|
||||
if (source){
|
||||
Gui::Application::Instance->getViewProvider(source)->show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,26 @@ public:
|
||||
bool onDelete(const std::vector<std::string> &);
|
||||
};
|
||||
|
||||
class ViewProviderOffset : public ViewProviderPart
|
||||
{
|
||||
PROPERTY_HEADER(PartGui::ViewProviderOffset);
|
||||
|
||||
public:
|
||||
/// constructor
|
||||
ViewProviderOffset();
|
||||
/// destructor
|
||||
virtual ~ViewProviderOffset();
|
||||
|
||||
/// grouping handling
|
||||
std::vector<App::DocumentObject*> claimChildren(void)const;
|
||||
void setupContextMenu(QMenu*, QObject*, const char*);
|
||||
bool onDelete(const std::vector<std::string> &);
|
||||
|
||||
protected:
|
||||
virtual bool setEdit(int ModNum);
|
||||
virtual void unsetEdit(int ModNum);
|
||||
};
|
||||
|
||||
} // namespace PartGui
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
<< "Part_SimpleCopy" << "Part_RefineShape" << "Part_CheckGeometry" << "Separator"
|
||||
<< "Part_Boolean" << "Part_CrossSections" << "Part_Extrude"
|
||||
<< "Part_Revolve" << "Part_Mirror" << "Part_Fillet" << "Part_Chamfer"
|
||||
<< "Part_RuledSurface" << "Part_Loft" << "Part_Sweep";
|
||||
<< "Part_RuledSurface" << "Part_Loft" << "Part_Sweep" << "Part_Offset";
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -89,7 +89,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
Gui::ToolBarItem* tool = new Gui::ToolBarItem(root);
|
||||
tool->setCommand("Part tools");
|
||||
*tool << "Part_Extrude" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet"
|
||||
<< "Part_Chamfer" << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep";
|
||||
<< "Part_Chamfer" << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep"
|
||||
<< "Part_Offset";
|
||||
|
||||
Gui::ToolBarItem* boolop = new Gui::ToolBarItem(root);
|
||||
boolop->setCommand("Boolean");
|
||||
|
||||
@@ -119,8 +119,6 @@ def legend(status=True, pos=None, fontsize=None):
|
||||
if not plt:
|
||||
return
|
||||
plt.legend = status
|
||||
if pos:
|
||||
plt.legPos = pos
|
||||
if fontsize:
|
||||
plt.legSiz = fontsize
|
||||
# Hide all legends
|
||||
@@ -137,8 +135,19 @@ def legend(status=True, pos=None, fontsize=None):
|
||||
if l.name != None:
|
||||
handles.append(l.line)
|
||||
names.append(l.name)
|
||||
# Show the legend
|
||||
l = axes.legend(handles, names, bbox_to_anchor=plt.legPos)
|
||||
# Show the legend (at selected position or at best)
|
||||
if pos:
|
||||
l = axes.legend(handles, names, bbox_to_anchor=pos)
|
||||
plt.legPos = pos
|
||||
else:
|
||||
l = axes.legend(handles, names, loc='best')
|
||||
# Update canvas in order to compute legend data
|
||||
plt.canvas.draw()
|
||||
# Get resultant position
|
||||
fax = axes.get_frame().get_extents()
|
||||
fl = l.get_frame()
|
||||
plt.legPos = ((fl._x+fl._width-fax.x0) / fax.width, (fl._y+fl._height-fax.y0) / fax.height)
|
||||
# Set fontsize
|
||||
for t in l.get_texts():
|
||||
t.set_fontsize(plt.legSiz)
|
||||
plt.update()
|
||||
@@ -276,7 +285,7 @@ class Plot(QtGui.QWidget):
|
||||
# Indicators
|
||||
self.skip = False
|
||||
self.legend = False
|
||||
self.legPos = (1.0, 1.0)
|
||||
self.legPos = (1.0,1.0)
|
||||
self.legSiz = 14
|
||||
self.grid = False
|
||||
|
||||
@@ -293,7 +302,7 @@ class Plot(QtGui.QWidget):
|
||||
if not self.skip:
|
||||
self.skip = True
|
||||
if self.legend:
|
||||
legend(self.legend)
|
||||
legend(self.legend, self.legPos, self.legSiz)
|
||||
self.canvas.draw()
|
||||
self.skip = False
|
||||
|
||||
|
||||
@@ -135,13 +135,13 @@ INSTALL(
|
||||
FILES
|
||||
${ShipIcons_SRCS}
|
||||
DESTINATION
|
||||
Mod/Ship/Icons
|
||||
Mod/Ship/resources/icons
|
||||
)
|
||||
INSTALL(
|
||||
FILES
|
||||
${ShipExamples_SRCS}
|
||||
DESTINATION
|
||||
Mod/Ship/Examples
|
||||
Mod/Ship/resources/examples
|
||||
)
|
||||
INSTALL(
|
||||
FILES
|
||||
|
||||
@@ -27,11 +27,8 @@ import math
|
||||
from PyQt4 import QtGui,QtCore
|
||||
# FreeCAD modules
|
||||
import FreeCAD,FreeCADGui
|
||||
from FreeCAD import Base, Vector
|
||||
import Part, Image, ImageGui
|
||||
# FreeCADShip modules
|
||||
from shipUtils import Paths
|
||||
import Tools
|
||||
|
||||
header = """ #################################################################
|
||||
|
||||
@@ -47,32 +44,13 @@ header = """ #################################################################
|
||||
"""
|
||||
|
||||
class Plot(object):
|
||||
def __init__(self, ship, trim, drafts):
|
||||
def __init__(self, ship, trim, points):
|
||||
""" Constructor. performs plot and show it (Using pyxplot).
|
||||
@param ship Selected ship instance
|
||||
@param trim Trim in degrees.
|
||||
@param drafts List of drafts to be performed.
|
||||
@param points List of computed hydrostatics.
|
||||
"""
|
||||
# Compute data
|
||||
# Get external faces
|
||||
faces = self.externalFaces(ship.Shape)
|
||||
if len(faces) == 0:
|
||||
msg = QtGui.QApplication.translate("ship_console", "Can't detect external faces from ship object",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
FreeCAD.Console.PrintError(msg + '\n')
|
||||
return
|
||||
else:
|
||||
faces = Part.makeShell(faces)
|
||||
# Print data
|
||||
msg = QtGui.QApplication.translate("ship_console", "Computing hydrostatics",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
FreeCAD.Console.PrintMessage(msg + '...\n')
|
||||
self.points = []
|
||||
for i in range(0,len(drafts)):
|
||||
FreeCAD.Console.PrintMessage("\t%d / %d\n" % (i+1, len(drafts)))
|
||||
draft = drafts[i]
|
||||
point = Tools.Point(ship,faces,draft,trim)
|
||||
self.points.append(point)
|
||||
self.points = points[:]
|
||||
# Try to plot
|
||||
self.plotVolume()
|
||||
self.plotStability()
|
||||
@@ -80,7 +58,7 @@ class Plot(object):
|
||||
# Save data
|
||||
if self.createDirectory():
|
||||
return
|
||||
if self.saveData(ship, trim, drafts):
|
||||
if self.saveData(ship, trim):
|
||||
return
|
||||
|
||||
def plotVolume(self):
|
||||
@@ -345,11 +323,10 @@ class Plot(object):
|
||||
FreeCAD.Console.PrintError(msg + ':\n\t' + "\'"+ self.path + "\'\n")
|
||||
return False
|
||||
|
||||
def saveData(self, ship, trim, drafts):
|
||||
def saveData(self, ship, trim):
|
||||
""" Write data file.
|
||||
@param ship Selected ship instance
|
||||
@param trim Trim in degrees.
|
||||
@param drafts List of drafts to be performed.
|
||||
@return True if error happens.
|
||||
"""
|
||||
# Open the file
|
||||
@@ -380,7 +357,7 @@ class Plot(object):
|
||||
Output.write(" #\n")
|
||||
Output.write(" #################################################################\n")
|
||||
# Print data
|
||||
for i in range(0,len(drafts)):
|
||||
for i in range(0,len(self.points)):
|
||||
point = self.points[i]
|
||||
string = "%f %f %f %f %f %f %f %f %f %f %f\n" % (point.disp, point.draft, point.wet, point.mom, point.xcb, point.farea, point.KBt, point.BMt, point.Cb, point.Cf, point.Cm)
|
||||
Output.write(string)
|
||||
@@ -392,87 +369,3 @@ class Plot(object):
|
||||
FreeCAD.Console.PrintMessage(msg + ':\n\t' + "\'"+ self.dataFile + "\'\n")
|
||||
return False
|
||||
|
||||
def lineFaceSection(self,line,surface):
|
||||
""" Returns the point of section of a line with a face
|
||||
@param line Line object, that can be a curve.
|
||||
@param surface Surface object (must be a Part::Shape)
|
||||
@return Section points array, [] if line don't cut surface
|
||||
"""
|
||||
# Get initial data
|
||||
result = []
|
||||
vertexes = line.Vertexes
|
||||
nVertex = len(vertexes)
|
||||
# Perform the cut
|
||||
section = line.cut(surface)
|
||||
# Filter all old points
|
||||
points = section.Vertexes
|
||||
return points
|
||||
|
||||
def externalFaces(self, shape):
|
||||
""" Returns detected external faces.
|
||||
@param shape Shape where external faces wanted.
|
||||
@return List of external faces detected.
|
||||
"""
|
||||
result = []
|
||||
faces = shape.Faces
|
||||
bbox = shape.BoundBox
|
||||
L = bbox.XMax - bbox.XMin
|
||||
B = bbox.YMax - bbox.YMin
|
||||
T = bbox.ZMax - bbox.ZMin
|
||||
dist = math.sqrt(L*L + B*B + T*T)
|
||||
msg = QtGui.QApplication.translate("ship_console", "Computing external faces",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
FreeCAD.Console.PrintMessage(msg + '...\n')
|
||||
# Valid/unvalid faces detection loop
|
||||
for i in range(0,len(faces)):
|
||||
FreeCAD.Console.PrintMessage("\t%d / %d\n" % (i+1, len(faces)))
|
||||
f = faces[i]
|
||||
# Create a line normal to surface at middle point
|
||||
u = 0.0
|
||||
v = 0.0
|
||||
try:
|
||||
surf = f.Surface
|
||||
u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1])
|
||||
v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1])
|
||||
except:
|
||||
cog = f.CenterOfMass
|
||||
[u,v] = f.Surface.parameter(cog)
|
||||
p0 = f.valueAt(u,v)
|
||||
try:
|
||||
n = f.normalAt(u,v).normalize()
|
||||
except:
|
||||
continue
|
||||
p1 = p0 + n.multiply(1.5*dist)
|
||||
line = Part.makeLine(p0, p1)
|
||||
# Look for faces in front of this
|
||||
nPoints = 0
|
||||
for j in range(0,len(faces)):
|
||||
f2 = faces[j]
|
||||
section = self.lineFaceSection(line, f2)
|
||||
if len(section) <= 2:
|
||||
continue
|
||||
# Add points discarding start and end
|
||||
nPoints = nPoints + len(section) - 2
|
||||
# In order to avoid special directions we can modify line
|
||||
# normal a little bit.
|
||||
angle = 5
|
||||
line.rotate(p0,Vector(1,0,0),angle)
|
||||
line.rotate(p0,Vector(0,1,0),angle)
|
||||
line.rotate(p0,Vector(0,0,1),angle)
|
||||
nPoints2 = 0
|
||||
for j in range(0,len(faces)):
|
||||
if i == j:
|
||||
continue
|
||||
f2 = faces[j]
|
||||
section = self.lineFaceSection(line, f2)
|
||||
if len(section) <= 2:
|
||||
continue
|
||||
# Add points discarding start and end
|
||||
nPoints2 = nPoints + len(section) - 2
|
||||
# If the number of intersection points is pair, is a
|
||||
# external face. So if we found an odd points intersection,
|
||||
# face must be discarded.
|
||||
if (nPoints % 2) or (nPoints2 % 2):
|
||||
continue
|
||||
result.append(f)
|
||||
return result
|
||||
|
||||
@@ -25,6 +25,8 @@ import math
|
||||
# FreeCAD modules
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
from FreeCAD import Base, Vector
|
||||
import Part
|
||||
# Qt library
|
||||
from PyQt4 import QtGui,QtCore
|
||||
# Module
|
||||
@@ -37,10 +39,13 @@ class TaskPanel:
|
||||
def __init__(self):
|
||||
self.ui = Paths.modulePath() + "/shipHydrostatics/TaskPanel.ui"
|
||||
self.ship = None
|
||||
self.running = False
|
||||
|
||||
def accept(self):
|
||||
if not self.ship:
|
||||
return False
|
||||
if self.running:
|
||||
return
|
||||
self.save()
|
||||
draft = self.form.minDraft.value()
|
||||
drafts = [draft]
|
||||
@@ -48,10 +53,45 @@ class TaskPanel:
|
||||
for i in range(1,self.form.nDraft.value()):
|
||||
draft = draft + dDraft
|
||||
drafts.append(draft)
|
||||
PlotAux.Plot(self.ship, self.form.trim.value(), drafts)
|
||||
# Compute data
|
||||
# Get external faces
|
||||
self.loop=QtCore.QEventLoop()
|
||||
self.timer=QtCore.QTimer()
|
||||
self.timer.setSingleShot(True)
|
||||
QtCore.QObject.connect(self.timer,QtCore.SIGNAL("timeout()"),self.loop,QtCore.SLOT("quit()"))
|
||||
self.running = True
|
||||
faces = self.externalFaces(self.ship.Shape)
|
||||
if not self.running:
|
||||
return False
|
||||
if len(faces) == 0:
|
||||
msg = QtGui.QApplication.translate("ship_console", "Can't detect external faces from ship object",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
App.Console.PrintError(msg + '\n')
|
||||
return False
|
||||
faces = Part.makeShell(faces)
|
||||
# Get hydrostatics
|
||||
msg = QtGui.QApplication.translate("ship_console", "Computing hydrostatics",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
App.Console.PrintMessage(msg + '...\n')
|
||||
points = []
|
||||
for i in range(0,len(drafts)):
|
||||
App.Console.PrintMessage("\t%d / %d\n" % (i+1, len(drafts)))
|
||||
draft = drafts[i]
|
||||
point = Tools.Point(self.ship,faces,draft,self.form.trim.value())
|
||||
points.append(point)
|
||||
self.timer.start(0.0)
|
||||
self.loop.exec_()
|
||||
if(not self.running):
|
||||
break
|
||||
PlotAux.Plot(self.ship, self.form.trim.value(), points)
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
if not self.ship:
|
||||
return False
|
||||
if self.running:
|
||||
self.running = False
|
||||
return
|
||||
return True
|
||||
|
||||
def clicked(self, index):
|
||||
@@ -235,6 +275,95 @@ class TaskPanel:
|
||||
self.ship.addProperty("App::PropertyInteger","HydrostaticsNDraft","Ship", tooltip)
|
||||
self.ship.HydrostaticsNDraft = self.form.nDraft.value()
|
||||
|
||||
def lineFaceSection(self,line,surface):
|
||||
""" Returns the point of section of a line with a face
|
||||
@param line Line object, that can be a curve.
|
||||
@param surface Surface object (must be a Part::Shape)
|
||||
@return Section points array, [] if line don't cut surface
|
||||
"""
|
||||
# Get initial data
|
||||
result = []
|
||||
vertexes = line.Vertexes
|
||||
nVertex = len(vertexes)
|
||||
# Perform the cut
|
||||
section = line.cut(surface)
|
||||
# Filter all old points
|
||||
points = section.Vertexes
|
||||
return points
|
||||
|
||||
def externalFaces(self, shape):
|
||||
""" Returns detected external faces.
|
||||
@param shape Shape where external faces wanted.
|
||||
@return List of external faces detected.
|
||||
"""
|
||||
result = []
|
||||
faces = shape.Faces
|
||||
bbox = shape.BoundBox
|
||||
L = bbox.XMax - bbox.XMin
|
||||
B = bbox.YMax - bbox.YMin
|
||||
T = bbox.ZMax - bbox.ZMin
|
||||
dist = math.sqrt(L*L + B*B + T*T)
|
||||
msg = QtGui.QApplication.translate("ship_console", "Computing external faces",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
App.Console.PrintMessage(msg + '...\n')
|
||||
# Valid/unvalid faces detection loop
|
||||
for i in range(0,len(faces)):
|
||||
App.Console.PrintMessage("\t%d / %d\n" % (i+1, len(faces)))
|
||||
f = faces[i]
|
||||
# Create a line normal to surface at middle point
|
||||
u = 0.0
|
||||
v = 0.0
|
||||
try:
|
||||
surf = f.Surface
|
||||
u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1])
|
||||
v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1])
|
||||
except:
|
||||
cog = f.CenterOfMass
|
||||
[u,v] = f.Surface.parameter(cog)
|
||||
p0 = f.valueAt(u,v)
|
||||
try:
|
||||
n = f.normalAt(u,v).normalize()
|
||||
except:
|
||||
continue
|
||||
p1 = p0 + n.multiply(1.5*dist)
|
||||
line = Part.makeLine(p0, p1)
|
||||
# Look for faces in front of this
|
||||
nPoints = 0
|
||||
for j in range(0,len(faces)):
|
||||
f2 = faces[j]
|
||||
section = self.lineFaceSection(line, f2)
|
||||
if len(section) <= 2:
|
||||
continue
|
||||
# Add points discarding start and end
|
||||
nPoints = nPoints + len(section) - 2
|
||||
# In order to avoid special directions we can modify line
|
||||
# normal a little bit.
|
||||
angle = 5
|
||||
line.rotate(p0,Vector(1,0,0),angle)
|
||||
line.rotate(p0,Vector(0,1,0),angle)
|
||||
line.rotate(p0,Vector(0,0,1),angle)
|
||||
nPoints2 = 0
|
||||
for j in range(0,len(faces)):
|
||||
if i == j:
|
||||
continue
|
||||
f2 = faces[j]
|
||||
section = self.lineFaceSection(line, f2)
|
||||
if len(section) <= 2:
|
||||
continue
|
||||
# Add points discarding start and end
|
||||
nPoints2 = nPoints + len(section) - 2
|
||||
# If the number of intersection points is pair, is a
|
||||
# external face. So if we found an odd points intersection,
|
||||
# face must be discarded.
|
||||
if (nPoints % 2) or (nPoints2 % 2):
|
||||
continue
|
||||
result.append(f)
|
||||
self.timer.start(0.0)
|
||||
self.loop.exec_()
|
||||
if(not self.running):
|
||||
break
|
||||
return result
|
||||
|
||||
def createTask():
|
||||
panel = TaskPanel()
|
||||
Gui.Control.showDialog(panel)
|
||||
|
||||
@@ -39,10 +39,13 @@ class TaskPanel:
|
||||
self.ui = Paths.modulePath() + "/tankGZ/TaskPanel.ui"
|
||||
self.ship = None
|
||||
self.tanks = {}
|
||||
self.running = False
|
||||
|
||||
def accept(self):
|
||||
if not self.ship:
|
||||
return False
|
||||
if self.running:
|
||||
return
|
||||
# Get general data
|
||||
disp = self.computeDisplacement()
|
||||
draft = self.computeDraft(disp[0], self.form.trim.value())
|
||||
@@ -57,16 +60,28 @@ class TaskPanel:
|
||||
msg = QtGui.QApplication.translate("ship_console","Computing GZ",
|
||||
None,QtGui.QApplication.UnicodeUTF8)
|
||||
App.Console.PrintMessage(msg + "...\n")
|
||||
loop=QtCore.QEventLoop()
|
||||
timer=QtCore.QTimer()
|
||||
timer.setSingleShot(True)
|
||||
QtCore.QObject.connect(timer,QtCore.SIGNAL("timeout()"),loop,QtCore.SLOT("quit()"))
|
||||
self.running = True
|
||||
for i in range(0, nRoll):
|
||||
App.Console.PrintMessage("\t%d/%d\n" % (i+1,nRoll))
|
||||
roll.append(i*dRoll)
|
||||
GZ.append(self.computeGZ(draft[0], trim, roll[-1]))
|
||||
timer.start(0.0)
|
||||
loop.exec_()
|
||||
if(not self.running):
|
||||
break
|
||||
PlotAux.Plot(roll, GZ, disp[0]/1000.0, draft[0], trim)
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
if not self.ship:
|
||||
return False
|
||||
if self.running:
|
||||
self.running = False
|
||||
return
|
||||
return True
|
||||
|
||||
def clicked(self, index):
|
||||
|
||||
Reference in New Issue
Block a user