From 1396d7a3a22fa0becd8b43f296ceb2ce4b144967 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 18 Nov 2012 14:46:10 +0100 Subject: [PATCH 01/22] Included legend best location capabilities --- src/Mod/Plot/Plot.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Mod/Plot/Plot.py b/src/Mod/Plot/Plot.py index 59020ab229..c943363a60 100644 --- a/src/Mod/Plot/Plot.py +++ b/src/Mod/Plot/Plot.py @@ -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 From 51a6d34c8b6bb7506a2a086af95b8f932ddf722e Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 19 Nov 2012 13:06:16 +0100 Subject: [PATCH 02/22] Fix problem with undo/redo with python-based features --- src/Gui/ViewProviderPythonFeature.cpp | 31 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Gui/ViewProviderPythonFeature.cpp b/src/Gui/ViewProviderPythonFeature.cpp index 144d158159..b008fd04ac 100644 --- a/src/Gui/ViewProviderPythonFeature.cpp +++ b/src/Gui/ViewProviderPythonFeature.cpp @@ -25,6 +25,8 @@ #ifndef _PreComp_ # include +# include +# include # include # include # include @@ -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(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 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(prop)->fromString(jt->second); - static_cast(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(prop)->toString(); - proxyMap[doc][docobj] = proxy; + proxyMap[doc][docobj] = prop->Copy(); } } catch (Py::Exception& e) { From d4f1250453eee6c09b598859f63fb52900194c45 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 20 Nov 2012 13:16:15 +0100 Subject: [PATCH 03/22] 0000827: Sketch copy deletes all constraints --- src/App/Document.cpp | 46 +++++++++++++--------- src/App/Document.h | 3 +- src/Base/Reader.cpp | 9 +++++ src/Base/Reader.h | 2 + src/Gui/MergeDocuments.cpp | 80 +++++++++++--------------------------- src/Gui/MergeDocuments.h | 4 ++ 6 files changed, 67 insertions(+), 77 deletions(-) diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 3ab7c2caeb..7f267da34f 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -771,32 +771,28 @@ void Document::exportObjects(const std::vector& obj, } std::vector -Document::importObjects(std::istream& input) +Document::readObjects(Base::XMLReader& reader) { std::vector objs; - zipios::ZipInputStream zipstream(input); - Base::XMLReader reader("", zipstream); - - int i,Cnt; - reader.readElement("Document"); - long scheme = reader.getAttributeAsInteger("SchemaVersion"); - reader.DocumentSchema = scheme; // read the object types - std::map nameMap; reader.readElement("Objects"); - Cnt = reader.getAttributeAsInteger("Count"); - for (i=0 ;igetNameInDocument(); + reader.addName(name.c_str(), o->getNameInDocument()); } catch (Base::Exception&) { Base::Console().Message("Cannot create object '%s'\n", name.c_str()); @@ -807,9 +803,9 @@ Document::importObjects(std::istream& input) // read the features itself reader.readElement("ObjectData"); Cnt = reader.getAttributeAsInteger("Count"); - for (i=0 ;iStatusBits.set(4); @@ -820,12 +816,26 @@ Document::importObjects(std::istream& input) } reader.readEndElement("ObjectData"); + return objs; +} + +std::vector +Document::importObjects(Base::XMLReader& reader) +{ + reader.readElement("Document"); + long scheme = reader.getAttributeAsInteger("SchemaVersion"); + reader.DocumentSchema = scheme; + + std::vector objs = readObjects(reader); + reader.readEndElement("Document"); signalImportObjects(objs, reader); - reader.readFiles(zipstream); + // reset all touched - for (std::vector::iterator it= objs.begin();it!=objs.end();++it) + for (std::vector::iterator it= objs.begin();it!=objs.end();++it) { + (*it)->onDocumentRestored(); (*it)->purgeTouched(); + } return objs; } diff --git a/src/App/Document.h b/src/App/Document.h index 9b705e8c9d..740ab75ba0 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -115,7 +115,7 @@ public: void restore (void); void exportObjects(const std::vector&, std::ostream&); void exportGraphviz(std::ostream&); - std::vector importObjects(std::istream&); + std::vector 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,7 @@ protected: /// checks if a valid transaction is open void _checkTransaction(void); void breakDependency(DocumentObject* pcObject, bool clear); + std::vector readObjects(Base::XMLReader& reader); void onChanged(const Property* prop); /// callback from the Document objects before property will be changed diff --git a/src/Base/Reader.cpp b/src/Base/Reader.cpp index 6791b99f62..496f583da8 100644 --- a/src/Base/Reader.cpp +++ b/src/Base/Reader.cpp @@ -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 // --------------------------------------------------------------------------- diff --git a/src/Base/Reader.h b/src/Base/Reader.h index 7e55575d42..f35ff379d7 100644 --- a/src/Base/Reader.h +++ b/src/Base/Reader.h @@ -159,6 +159,8 @@ public: /// get all registered file names const std::vector& 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 diff --git a/src/Gui/MergeDocuments.cpp b/src/Gui/MergeDocuments.cpp index 52713fa474..9b53d960cb 100644 --- a/src/Gui/MergeDocuments.cpp +++ b/src/Gui/MergeDocuments.cpp @@ -42,10 +42,22 @@ namespace Gui { class XMLMergeReader : public Base::XMLReader { public: - XMLMergeReader(const std::map& name, const char* FileName, std::istream& str) + XMLMergeReader(std::map& 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::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& nameMap; + std::map& nameMap; typedef std::pair PropertyTag; std::stack propertyStack; }; @@ -104,64 +116,14 @@ unsigned int MergeDocuments::getMemSize (void) const std::vector MergeDocuments::importObjects(std::istream& input) { - //std::map nameMap; - std::vector objs; - zipios::ZipInputStream zipstream(input); - XMLMergeReader reader(nameMap,"", zipstream); + this->nameMap.clear(); + this->stream = new zipios::ZipInputStream(input); + XMLMergeReader reader(this->nameMap,"", *stream); + std::vector 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 ;iaddObject(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 ;igetObject(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::iterator it= objs.begin();it!=objs.end();++it) - (*it)->purgeTouched(); return objs; } @@ -173,6 +135,8 @@ void MergeDocuments::importObject(const std::vector& o, Ba if (vp) vp->hide(); } Restore(r); + + r.readFiles(*this->stream); } void MergeDocuments::exportObject(const std::vector& o, Base::Writer & w) diff --git a/src/Gui/MergeDocuments.h b/src/Gui/MergeDocuments.h index 817ae91193..42bc252e02 100644 --- a/src/Gui/MergeDocuments.h +++ b/src/Gui/MergeDocuments.h @@ -27,6 +27,9 @@ #include #include +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 objects; From 7e2478faed1210b5616acd84f9eff4b814e379d6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 20 Nov 2012 15:39:02 +0100 Subject: [PATCH 04/22] Reduce code duplication --- src/App/Document.cpp | 84 +++++++++----------------------------------- src/App/Document.h | 1 + 2 files changed, 18 insertions(+), 67 deletions(-) diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 7f267da34f..abea1441bf 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -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() << "objectArray.size() <<"\">" << endl; - - writer.incInd(); // indention for 'Object type' - std::vector::const_iterator it; - for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) { - writer.Stream() << writer.ind() << "getTypeId().getName() << "\" " - << "name=\"" << (*it)->getNameInDocument() << "\" " - << "/>" << endl; - } - - writer.decInd(); // indention for 'Object type' - writer.Stream() << writer.ind() << "" << endl; - - // writing the features itself - writer.Stream() << writer.ind() << "objectArray.size() <<"\">" << endl; - - writer.incInd(); // indention for 'Object name' - for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) { - writer.Stream() << writer.ind() << "getNameInDocument() << "\">" << endl; - (*it)->Save(writer); - writer.Stream() << writer.ind() << "" << endl; - } - - writer.decInd(); // indention for 'Object name' - writer.Stream() << writer.ind() << "" << endl; - writer.decInd(); // indention for 'Objects count' + writeObjects(d->objectArray, writer); writer.Stream() << "" << 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 ;iStatusBits.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& obj, writer.Stream() << "" << endl; writer.Stream() << "" << endl; + // writing the object types + writeObjects(obj, writer); + writer.Stream() << "" << endl; + + // Hook for others to add further data. + signalExportObjects(obj, writer); + + // write additional files + writer.writeFiles(); +} + +void Document::writeObjects(const std::vector& obj, + Base::Writer &writer) const +{ // writing the features types writer.incInd(); // indention for 'Objects count' writer.Stream() << writer.ind() << "" << endl; @@ -741,7 +698,7 @@ void Document::exportObjects(const std::vector& obj, for (it = obj.begin(); it != obj.end(); ++it) { writer.Stream() << writer.ind() << "getTypeId().getName() << "\" " - << "name=\"" << (*it)->getNameInDocument() << "\" " + << "name=\"" << (*it)->getNameInDocument() << "\" " << "/>" << endl; } @@ -761,13 +718,6 @@ void Document::exportObjects(const std::vector& obj, writer.decInd(); // indention for 'Object name' writer.Stream() << writer.ind() << "" << endl; writer.decInd(); // indention for 'Objects count' - writer.Stream() << "" << endl; - - // Hook for others to add further data. - signalExportObjects(obj, writer); - - // write additional files - writer.writeFiles(); } std::vector diff --git a/src/App/Document.h b/src/App/Document.h index 740ab75ba0..c8dd9d830e 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -267,6 +267,7 @@ protected: void _checkTransaction(void); void breakDependency(DocumentObject* pcObject, bool clear); std::vector readObjects(Base::XMLReader& reader); + void writeObjects(const std::vector&, Base::Writer &writer) const; void onChanged(const Property* prop); /// callback from the Document objects before property will be changed From 4db40a3d35a449ad7df891c62459e63e23f2b3e0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 22 Nov 2012 15:17:32 +0100 Subject: [PATCH 05/22] Extend Action/Command framework to add arbitrary widgets --- src/Gui/Action.cpp | 29 ++++++++++++++++++++++++++++- src/Gui/Action.h | 4 ++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Gui/Action.cpp b/src/Gui/Action.cpp index 5e39c8a518..5cdc6d4127 100644 --- a/src/Gui/Action.cpp +++ b/src/Gui/Action.cpp @@ -31,6 +31,7 @@ # include # include # include +# include # include # include #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) diff --git a/src/Gui/Action.h b/src/Gui/Action.h index 0b33f57ece..34ce2dd525 100644 --- a/src/Gui/Action.h +++ b/src/Gui/Action.h @@ -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 actions() const; int checkedAction() const; @@ -107,6 +110,7 @@ public: public Q_SLOTS: void onActivated (); + void onActivated (int); void onActivated (QAction*); protected: From 9b51bc433c94a3052934f5733393082daf8c42e0 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Thu, 22 Nov 2012 19:25:54 +0100 Subject: [PATCH 06/22] Added real time reporting and cancel capabilities --- src/Mod/Ship/tankGZ/TaskPanel.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index ce7943139e..b2b1b7b92f 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -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): From c4146c1c870989863019e346843d8ac032d66e9a Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Thu, 22 Nov 2012 19:42:20 +0100 Subject: [PATCH 07/22] Moved hydrostatics computation from PlotAux to Task manager --- src/Mod/Ship/shipHydrostatics/PlotAux.py | 111 +-------------------- src/Mod/Ship/shipHydrostatics/TaskPanel.py | 108 +++++++++++++++++++- 2 files changed, 110 insertions(+), 109 deletions(-) diff --git a/src/Mod/Ship/shipHydrostatics/PlotAux.py b/src/Mod/Ship/shipHydrostatics/PlotAux.py index 316bed13e6..32b2f3fe30 100644 --- a/src/Mod/Ship/shipHydrostatics/PlotAux.py +++ b/src/Mod/Ship/shipHydrostatics/PlotAux.py @@ -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,14 @@ header = """ ################################################################# """ class Plot(object): - def __init__(self, ship, trim, drafts): + def __init__(self, ship, trim, drafts, 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() @@ -392,87 +371,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 diff --git a/src/Mod/Ship/shipHydrostatics/TaskPanel.py b/src/Mod/Ship/shipHydrostatics/TaskPanel.py index bc5e2d967f..dfd4588b27 100644 --- a/src/Mod/Ship/shipHydrostatics/TaskPanel.py +++ b/src/Mod/Ship/shipHydrostatics/TaskPanel.py @@ -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 @@ -48,7 +50,26 @@ 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 + faces = self.externalFaces(self.ship.Shape) + 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) + PlotAux.Plot(self.ship, self.form.trim.value(), drafts, points) return True def reject(self): @@ -235,6 +256,91 @@ 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) + return result + def createTask(): panel = TaskPanel() Gui.Control.showDialog(panel) From fbc06cafb64aa2d569fcdd967bfd782353dbf829 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Thu, 22 Nov 2012 19:58:19 +0100 Subject: [PATCH 08/22] Moved to real time reporting and cancel new stuff --- src/Mod/Ship/shipHydrostatics/PlotAux.py | 10 ++++----- src/Mod/Ship/shipHydrostatics/TaskPanel.py | 25 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Mod/Ship/shipHydrostatics/PlotAux.py b/src/Mod/Ship/shipHydrostatics/PlotAux.py index 32b2f3fe30..a71b50b231 100644 --- a/src/Mod/Ship/shipHydrostatics/PlotAux.py +++ b/src/Mod/Ship/shipHydrostatics/PlotAux.py @@ -44,11 +44,10 @@ header = """ ################################################################# """ class Plot(object): - def __init__(self, ship, trim, drafts, points): + 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. """ self.points = points[:] @@ -59,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): @@ -324,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 @@ -359,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) diff --git a/src/Mod/Ship/shipHydrostatics/TaskPanel.py b/src/Mod/Ship/shipHydrostatics/TaskPanel.py index dfd4588b27..84f3feca15 100644 --- a/src/Mod/Ship/shipHydrostatics/TaskPanel.py +++ b/src/Mod/Ship/shipHydrostatics/TaskPanel.py @@ -39,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] @@ -52,7 +55,14 @@ class TaskPanel: drafts.append(draft) # 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) @@ -69,10 +79,19 @@ class TaskPanel: draft = drafts[i] point = Tools.Point(self.ship,faces,draft,self.form.trim.value()) points.append(point) - PlotAux.Plot(self.ship, self.form.trim.value(), drafts, points) + 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): @@ -339,6 +358,10 @@ class TaskPanel: 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(): From be98bfc69d1b42f306738bef0f85bdc56cb01513 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 23 Nov 2012 12:29:54 +0100 Subject: [PATCH 09/22] Add missing break statement in switch block --- src/Mod/Part/App/PropertyTopoShape.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index f3e83a809a..718e7fee9d 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -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)); From 28193af5fddf5d358c0f249d2bc262ee2731df89 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 23 Nov 2012 19:23:10 +0100 Subject: [PATCH 10/22] Fix view fit for perspective camera by setting height angle to 45 deg --- src/Gui/View3DInventorViewer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 47f3b99910..6e17e1d72a 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -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(cam)->heightAngle = (float)(M_PI / 4.0); + // call the default implementation first to make sure everything is visible SoQtViewer::viewAll(); From 13f06b6a7a4c6362eaa833bc82e1fb50cc4750bb Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 16:54:15 -0200 Subject: [PATCH 11/22] Draft: Fixed a bug in Shape2Dview --- src/Mod/Draft/Draft.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 57a53e72a1..f70beccaeb 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -2799,7 +2799,10 @@ class _Shape2DView(_DraftObject): newedges = [] for e in oldedges: try: - newedges.append(e.Curve.toShape()) + if isinstance(e.Curve,Part.Line): + newedges.append(e.Curve.toShape()) + else: + newedges.append(e) except: print "Debug: error cleaning edge ",e return Part.makeCompound(newedges) From d662731f3f4ac500980f2abb466d2eeea8b2256f Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 18:34:44 -0200 Subject: [PATCH 12/22] 0000887: Draft BSpline bug --- src/Mod/Draft/DraftTools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index f9e84fb008..547c257c98 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -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: From c9164bb9d0571222d9b0b5323c2c2e9c6ff9db9d Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 18:36:43 -0200 Subject: [PATCH 13/22] Draft: Small fix to work without GUI --- src/Mod/Draft/Draft.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index f70beccaeb..d54523a85f 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -3079,6 +3079,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 From 8457014992e80d7793a0a4325324cdc14d85992d Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 19:22:00 -0200 Subject: [PATCH 14/22] Draft: Optimization in Shape2DView --- src/Mod/Draft/Draft.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index d54523a85f..c61ca642f9 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -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): @@ -2807,8 +2810,22 @@ class _Shape2DView(_DraftObject): 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 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": @@ -2831,9 +2848,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: @@ -2849,9 +2864,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: @@ -2861,9 +2874,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): From b01a35ab396036a12198b330199759560615e350 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 20:44:01 -0200 Subject: [PATCH 15/22] Draft: small bugfix in shape2dview --- src/Mod/Draft/Draft.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index c61ca642f9..6fbd2d2ba7 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -2796,14 +2796,24 @@ 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: 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()) + # TODO: treat ellipses and bsplines else: newedges.append(e) except: @@ -2822,6 +2832,7 @@ class _Shape2DView(_DraftObject): 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): From 33c7a90e43747c6ea5aebe1cc5caa4c37bf690f3 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 23 Nov 2012 22:35:54 -0200 Subject: [PATCH 16/22] Draft: better cleaning of shape2Dview --- src/Mod/Draft/Draft.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 6fbd2d2ba7..9eb6d9bb30 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -2813,7 +2813,18 @@ class _Shape2DView(_DraftObject): newedges.append(a) else: newedges.append(e.Curve.toShape()) - # TODO: treat ellipses and bsplines + 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: From f95e99084448551fc0dd5dddc9e3591b74cd0ebd Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sat, 24 Nov 2012 14:21:42 -0200 Subject: [PATCH 17/22] Draft: Bugfix in parametric dimensions --- src/Mod/Draft/DraftTools.py | 13 +++++++------ src/Mod/Draft/DraftTrackers.py | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 547c257c98..f3378ecaa6 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -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 diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index be15757b6e..fcf88c25f2 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -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] From 7ae0bb2f339e4009b6c9701aa11970aef75c86f0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 24 Nov 2012 20:45:47 +0100 Subject: [PATCH 18/22] Make more consistent method names in TopoShape --- src/Mod/Part/App/TopoShape.cpp | 95 ++++++++++++++++++++++++++--- src/Mod/Part/App/TopoShape.h | 10 +-- src/Mod/Part/App/TopoShapePy.xml | 2 +- src/Mod/Part/App/TopoShapePyImp.cpp | 23 +++++-- 4 files changed, 110 insertions(+), 20 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 68c731fdc9..47a42cb0af 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -1690,21 +1690,98 @@ 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 +#include -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 0 + 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); + 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) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 26e891a9ca..0369682ff3 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -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*/ diff --git a/src/Mod/Part/App/TopoShapePy.xml b/src/Mod/Part/App/TopoShapePy.xml index 222bfd2041..bf4dffa9cc 100644 --- a/src/Mod/Part/App/TopoShapePy.xml +++ b/src/Mod/Part/App/TopoShapePy.xml @@ -189,7 +189,7 @@ the underlying geometry. - makeThickness(List of shapes, Ofset (Float), Tolerance (Float)) -> Shape + 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. diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index 1dc8bfc3a5..bc74344ddd 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -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) { From 16384de1bc277ae3ab73161a67727153e12b8d8b Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 24 Nov 2012 21:24:58 +0100 Subject: [PATCH 19/22] Fill&sew shape and its offset --- src/Mod/Part/App/TopoShape.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 47a42cb0af..bd3746a262 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -1690,8 +1690,8 @@ TopoDS_Shape TopoShape::revolve(const gp_Ax1& axis, double d) const return mkRevol.Shape(); } -#include -#include +//#include +//#include TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersection, bool selfInter, short offsetMode, short join, @@ -1704,7 +1704,9 @@ TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersec const TopoDS_Shape& res = mkOffset.Shape(); if (!fill) return res; -#if 0 +#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(); @@ -1733,8 +1735,15 @@ TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersec const TopTools_ListOfShape& edges = img.Image(edge); TopTools_ListIteratorOfListOfShape it; it.Initialize(edges); - TopoDS_Face face = BRepFill::Face(TopoDS::Edge(edge), TopoDS::Edge(it.Value())); - builder.Add(shell, face); + 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); } } } From b8ca20504af56092ea2c5d43aa67029c5b7e2d95 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 24 Nov 2012 22:46:16 +0100 Subject: [PATCH 20/22] Offset function --- src/Mod/Part/Gui/CMakeLists.txt | 5 ++ src/Mod/Part/Gui/Command.cpp | 30 +++++++ src/Mod/Part/Gui/TaskOffset.cpp | 133 ++++++++++++++++++++++++++++++++ src/Mod/Part/Gui/TaskOffset.h | 75 ++++++++++++++++++ src/Mod/Part/Gui/TaskOffset.ui | 115 +++++++++++++++++++++++++++ src/Mod/Part/Gui/Workbench.cpp | 2 +- 6 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Part/Gui/TaskOffset.cpp create mode 100644 src/Mod/Part/Gui/TaskOffset.h create mode 100644 src/Mod/Part/Gui/TaskOffset.ui diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index f6a41f935f..e138c13ec4 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -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 diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index 51f84675c6..d796c31c73 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -62,6 +62,7 @@ #include "TaskShapeBuilder.h" #include "TaskLoft.h" #include "TaskSweep.h" +#include "TaskOffset.h" #include "TaskCheckGeometry.h" @@ -990,6 +991,34 @@ 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) +{ + Gui::Control().showDialog(new PartGui::TaskOffset()); +} + +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 +1289,6 @@ void CreatePartCommands(void) rcCmdMgr.addCommand(new CmdPartBuilder()); rcCmdMgr.addCommand(new CmdPartLoft()); rcCmdMgr.addCommand(new CmdPartSweep()); + rcCmdMgr.addCommand(new CmdPartOffset()); rcCmdMgr.addCommand(new CmdCheckGeometry()); } diff --git a/src/Mod/Part/Gui/TaskOffset.cpp b/src/Mod/Part/Gui/TaskOffset.cpp new file mode 100644 index 0000000000..5324e17eaa --- /dev/null +++ b/src/Mod/Part/Gui/TaskOffset.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (c) 2012 Werner Mayer * + * * + * 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 +# include +#endif + +#include "ui_TaskOffset.h" +#include "TaskOffset.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +using namespace PartGui; + +class OffsetWidget::Private +{ +public: + Ui_TaskOffset ui; + std::string document; + Private() + { + } + ~Private() + { + } +}; + +/* TRANSLATOR PartGui::OffsetWidget */ + +OffsetWidget::OffsetWidget(QWidget* parent) + : d(new Private()) +{ + Gui::Application::Instance->runPythonCode("from FreeCAD import Base"); + Gui::Application::Instance->runPythonCode("import Part"); + + d->ui.setupUi(this); +} + +OffsetWidget::~OffsetWidget() +{ + delete d; +} + +bool OffsetWidget::accept() +{ + return true; +} + +bool OffsetWidget::reject() +{ + return true; +} + +void OffsetWidget::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + d->ui.retranslateUi(this); + } +} + + +/* TRANSLATOR PartGui::TaskOffset */ + +TaskOffset::TaskOffset() +{ + widget = new OffsetWidget(); + taskbox = new Gui::TaskView::TaskBox( + Gui::BitmapFactory().pixmap("Part_Offset"), + widget->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +TaskOffset::~TaskOffset() +{ +} + +void TaskOffset::open() +{ +} + +void TaskOffset::clicked(int) +{ +} + +bool TaskOffset::accept() +{ + return widget->accept(); +} + +bool TaskOffset::reject() +{ + return widget->reject(); +} + +#include "moc_TaskOffset.cpp" diff --git a/src/Mod/Part/Gui/TaskOffset.h b/src/Mod/Part/Gui/TaskOffset.h new file mode 100644 index 0000000000..d1dc8d47b6 --- /dev/null +++ b/src/Mod/Part/Gui/TaskOffset.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2012 Werner Mayer * + * * + * 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 +#include + +namespace PartGui { + +class OffsetWidget : public QWidget +{ + Q_OBJECT + +public: + OffsetWidget(QWidget* parent = 0); + ~OffsetWidget(); + + bool accept(); + bool reject(); + +private: + void changeEvent(QEvent *e); + +private: + class Private; + Private* d; +}; + +class TaskOffset : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskOffset(); + ~TaskOffset(); + +public: + void open(); + bool accept(); + bool reject(); + void clicked(int); + + QDialogButtonBox::StandardButtons getStandardButtons() const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + +private: + OffsetWidget* widget; + Gui::TaskView::TaskBox* taskbox; +}; + +} //namespace PartGui + +#endif // PARTGUI_TASKOFFSET_H diff --git a/src/Mod/Part/Gui/TaskOffset.ui b/src/Mod/Part/Gui/TaskOffset.ui new file mode 100644 index 0000000000..a2a0300c6e --- /dev/null +++ b/src/Mod/Part/Gui/TaskOffset.ui @@ -0,0 +1,115 @@ + + + PartGui::TaskOffset + + + + 0 + 0 + 256 + 217 + + + + Offset + + + + + + Offset + + + + + + + + + + Tolerance + + + + + + + + + + Mode + + + + + + + + Skin + + + + + Pipe + + + + + RectoVerso + + + + + + + + Join type + + + + + + + + Arc + + + + + Tangent + + + + + Intersection + + + + + + + + Intersection + + + + + + + Self-intersection + + + + + + + spinOffset + spinTolerance + comboBox + comboBox_2 + checkBox + checkBox_2 + + + + diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index ddd441b7d4..0798157317 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -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; } From 99340d448f26c89b601955eb4ba7255021577365 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 25 Nov 2012 12:16:48 +0100 Subject: [PATCH 21/22] Fixed bad resources installed location --- src/Mod/Ship/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index 9ca8c21bbc..8137d225dd 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -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 From b38b4b7159c8369aca616b8c1375f84723bdff20 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 25 Nov 2012 15:56:46 +0100 Subject: [PATCH 22/22] Offset function --- src/Gui/TaskView/TaskDialog.cpp | 15 ++ src/Gui/TaskView/TaskDialog.h | 1 + src/Mod/Part/App/AppPart.cpp | 1 + src/Mod/Part/App/PartFeatures.cpp | 65 ++++++ src/Mod/Part/App/PartFeatures.h | 34 ++++ src/Mod/Part/Gui/AppPartGui.cpp | 1 + src/Mod/Part/Gui/Command.cpp | 20 +- src/Mod/Part/Gui/Resources/Part.qrc | 1 + .../Part/Gui/Resources/icons/Part_Offset.svg | 185 ++++++++++++++++++ src/Mod/Part/Gui/TaskOffset.cpp | 109 ++++++++++- src/Mod/Part/Gui/TaskOffset.h | 16 +- src/Mod/Part/Gui/TaskOffset.ui | 63 +++--- src/Mod/Part/Gui/ViewProviderMirror.cpp | 82 ++++++++ src/Mod/Part/Gui/ViewProviderMirror.h | 20 ++ src/Mod/Part/Gui/Workbench.cpp | 3 +- 15 files changed, 581 insertions(+), 35 deletions(-) create mode 100644 src/Mod/Part/Gui/Resources/icons/Part_Offset.svg diff --git a/src/Gui/TaskView/TaskDialog.cpp b/src/Gui/TaskView/TaskDialog.cpp index b2a7830000..f539080566 100644 --- a/src/Gui/TaskView/TaskDialog.cpp +++ b/src/Gui/TaskView/TaskDialog.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include #endif #include "TaskDialog.h" @@ -57,6 +58,20 @@ const std::vector &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() diff --git a/src/Gui/TaskView/TaskDialog.h b/src/Gui/TaskView/TaskDialog.h index ec92a9dd2f..0a6321d931 100644 --- a/src/Gui/TaskView/TaskDialog.h +++ b/src/Gui/TaskView/TaskDialog.h @@ -60,6 +60,7 @@ public: ButtonPosition buttonPosition() const { return pos; } const std::vector &getDialogContent(void) const; + bool canClose() const; /// tells the framework which buttons whisched for the dialog virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index aee9f90b1c..a31156dc15 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -198,6 +198,7 @@ void PartExport initPart() Part::RuledSurface ::init(); Part::Loft ::init(); Part::Sweep ::init(); + Part::Offset ::init(); // Geometry types Part::Geometry ::init(); diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 45aec90d8e..71c732bf72 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -30,6 +30,7 @@ # include # include # include +# include #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(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; +} diff --git a/src/Mod/Part/App/PartFeatures.h b/src/Mod/Part/App/PartFeatures.h index e891cee593..1a26bd40ba 100644 --- a/src/Mod/Part/App/PartFeatures.h +++ b/src/Mod/Part/App/PartFeatures.h @@ -25,6 +25,7 @@ #define PART_FEATURES_H #include +#include #include 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 diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 48f9796756..e030eaae1d 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -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(); diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index d796c31c73..3db0f6c63c 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -62,7 +62,6 @@ #include "TaskShapeBuilder.h" #include "TaskLoft.h" #include "TaskSweep.h" -#include "TaskOffset.h" #include "TaskCheckGeometry.h" @@ -1007,7 +1006,24 @@ CmdPartOffset::CmdPartOffset() void CmdPartOffset::activated(int iMsg) { - Gui::Control().showDialog(new PartGui::TaskOffset()); + 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) diff --git a/src/Mod/Part/Gui/Resources/Part.qrc b/src/Mod/Part/Gui/Resources/Part.qrc index 8318095da1..61315158a7 100644 --- a/src/Mod/Part/Gui/Resources/Part.qrc +++ b/src/Mod/Part/Gui/Resources/Part.qrc @@ -19,6 +19,7 @@ icons/Part_Loft.svg icons/Part_Mirror.svg icons/Part_MirrorPNG.png + icons/Part_Offset.svg icons/Part_Revolve.svg icons/Part_RuledSurface.svg icons/Part_Section.svg diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg b/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg new file mode 100644 index 0000000000..47aeaaa8de --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/TaskOffset.cpp b/src/Mod/Part/Gui/TaskOffset.cpp index 5324e17eaa..3741b792b1 100644 --- a/src/Mod/Part/Gui/TaskOffset.cpp +++ b/src/Mod/Part/Gui/TaskOffset.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,7 @@ #include #include #include -#include +#include using namespace PartGui; @@ -52,7 +53,7 @@ class OffsetWidget::Private { public: Ui_TaskOffset ui; - std::string document; + Part::Offset* offset; Private() { } @@ -63,13 +64,17 @@ public: /* TRANSLATOR PartGui::OffsetWidget */ -OffsetWidget::OffsetWidget(QWidget* parent) +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() @@ -77,13 +82,102 @@ 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; } @@ -98,9 +192,9 @@ void OffsetWidget::changeEvent(QEvent *e) /* TRANSLATOR PartGui::TaskOffset */ -TaskOffset::TaskOffset() +TaskOffset::TaskOffset(Part::Offset* offset) { - widget = new OffsetWidget(); + widget = new OffsetWidget(offset); taskbox = new Gui::TaskView::TaskBox( Gui::BitmapFactory().pixmap("Part_Offset"), widget->windowTitle(), true, 0); @@ -112,6 +206,11 @@ TaskOffset::~TaskOffset() { } +Part::Offset* TaskOffset::getObject() const +{ + return widget->getObject(); +} + void TaskOffset::open() { } diff --git a/src/Mod/Part/Gui/TaskOffset.h b/src/Mod/Part/Gui/TaskOffset.h index d1dc8d47b6..019530438e 100644 --- a/src/Mod/Part/Gui/TaskOffset.h +++ b/src/Mod/Part/Gui/TaskOffset.h @@ -27,6 +27,7 @@ #include #include +namespace Part { class Offset; } namespace PartGui { class OffsetWidget : public QWidget @@ -34,11 +35,21 @@ class OffsetWidget : public QWidget Q_OBJECT public: - OffsetWidget(QWidget* parent = 0); + 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); @@ -53,7 +64,7 @@ class TaskOffset : public Gui::TaskView::TaskDialog Q_OBJECT public: - TaskOffset(); + TaskOffset(Part::Offset*); ~TaskOffset(); public: @@ -61,6 +72,7 @@ public: bool accept(); bool reject(); void clicked(int); + Part::Offset* getObject() const; QDialogButtonBox::StandardButtons getStandardButtons() const { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } diff --git a/src/Mod/Part/Gui/TaskOffset.ui b/src/Mod/Part/Gui/TaskOffset.ui index a2a0300c6e..3cd639c69c 100644 --- a/src/Mod/Part/Gui/TaskOffset.ui +++ b/src/Mod/Part/Gui/TaskOffset.ui @@ -7,7 +7,7 @@ 0 0 256 - 217 + 260 @@ -25,24 +25,14 @@ - - - Tolerance - - - - - - - Mode - - + + Skin @@ -60,15 +50,15 @@ - + Join type - - + + Arc @@ -86,29 +76,52 @@ - - + + Intersection - - + + Self-intersection + + + + Qt::Horizontal + + + + + + + Update view + + + true + + + + + + + Fill offset + + + spinOffset - spinTolerance - comboBox - comboBox_2 - checkBox - checkBox_2 + modeType + joinType + intersection + selfIntersection diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index b3c6d705ab..3e25af0e54 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -46,6 +46,7 @@ #include #include "ViewProviderMirror.h" #include "DlgFilletEdges.h" +#include "TaskOffset.h" using namespace PartGui; @@ -395,3 +396,84 @@ bool ViewProviderSweep::onDelete(const std::vector &) { 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(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(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 ViewProviderOffset::claimChildren() const +{ + std::vector child; + child.push_back(static_cast(getObject())->Source.getValue()); + return child; +} + +bool ViewProviderOffset::onDelete(const std::vector &) +{ + // get the support and Sketch + Part::Offset* offset = static_cast(getObject()); + App::DocumentObject* source = offset->Source.getValue(); + if (source){ + Gui::Application::Instance->getViewProvider(source)->show(); + } + + return true; +} diff --git a/src/Mod/Part/Gui/ViewProviderMirror.h b/src/Mod/Part/Gui/ViewProviderMirror.h index aae6a79aa6..407e06851d 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.h +++ b/src/Mod/Part/Gui/ViewProviderMirror.h @@ -138,6 +138,26 @@ public: bool onDelete(const std::vector &); }; +class ViewProviderOffset : public ViewProviderPart +{ + PROPERTY_HEADER(PartGui::ViewProviderOffset); + +public: + /// constructor + ViewProviderOffset(); + /// destructor + virtual ~ViewProviderOffset(); + + /// grouping handling + std::vector claimChildren(void)const; + void setupContextMenu(QMenu*, QObject*, const char*); + bool onDelete(const std::vector &); + +protected: + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); +}; + } // namespace PartGui diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index 0798157317..3563c82ed6 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -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");