Merge branch 'master' of git://free-cad.git.sourceforge.net/gitroot/free-cad/free-cad

This commit is contained in:
jrheinlaender
2012-11-25 20:15:47 +04:30
39 changed files with 1413 additions and 321 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -198,6 +198,7 @@ void PartExport initPart()
Part::RuledSurface ::init();
Part::Loft ::init();
Part::Sweep ::init();
Part::Offset ::init();
// Geometry types
Part::Geometry ::init();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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"

View 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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