diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 47f3b9991..6e17e1d72 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(); diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 57a53e72a..9eb6d9bb3 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): @@ -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 diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index f9e84fb00..f3378ecaa 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: @@ -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 be15757b6..fcf88c25f 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] diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index f3e83a809..718e7fee9 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)); diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 68c731fdc..bd3746a26 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -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 +//#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 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) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 26e891a9c..0369682ff 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 222bfd204..bf4dffa9c 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 1dc8bfc3a..bc74344dd 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) { diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index f6a41f935..e138c13ec 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 51f84675c..d796c31c7 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 000000000..5324e17ea --- /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 000000000..d1dc8d47b --- /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 000000000..a2a0300c6 --- /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 ddd441b7d..079815731 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; } diff --git a/src/Mod/Ship/shipHydrostatics/PlotAux.py b/src/Mod/Ship/shipHydrostatics/PlotAux.py index 316bed13e..a71b50b23 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,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 diff --git a/src/Mod/Ship/shipHydrostatics/TaskPanel.py b/src/Mod/Ship/shipHydrostatics/TaskPanel.py index bc5e2d967..84f3feca1 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 @@ -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) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index ce7943139..b2b1b7b92 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):