From 48f06f64b8eef2f94b34c507ad945874bdc39881 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 18:49:17 +0430 Subject: [PATCH 01/15] Fixed bug that showed wrong scaling (m instead of mm) on x axis --- src/Mod/PartDesign/WizardShaft/ShaftDiagram.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py index 74962dded..01e12b5a5 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py @@ -88,4 +88,6 @@ class Diagram: del self.thePlot.series[0] self.thePlot.update() + self.xpoints = [p * self.xscale for p in self.xpoints] + self.ypoints = [p * self.yscale for p in self.ypoints] self.thePlot.plot(self.xpoints, self.ypoints) From 3a8b127e6aae71ac939e5224e58bcf2c749f9f0b Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 18:53:26 +0430 Subject: [PATCH 02/15] Added inner diameter parameter for shaft wizard --- src/Mod/PartDesign/WizardShaft/Shaft.py | 17 +++++--- .../PartDesign/WizardShaft/ShaftFeature.py | 24 +++++++---- .../WizardShaft/WizardShaftTable.py | 41 ++++++++++++++----- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/Mod/PartDesign/WizardShaft/Shaft.py b/src/Mod/PartDesign/WizardShaft/Shaft.py index 78716a126..e426812c8 100644 --- a/src/Mod/PartDesign/WizardShaft/Shaft.py +++ b/src/Mod/PartDesign/WizardShaft/Shaft.py @@ -28,13 +28,15 @@ from ShaftDiagram import Diagram class ShaftSegment: length = 0.0 diameter = 0.0 + innerdiameter = 0.0 loadType = "None" loadSize = 0.0 loadLocation = 0.0 - def __init__(self, l, d): + def __init__(self, l, d, di): self.length = l self.diameter = d + self.innerdiameter = di class Shaft: "The axis of the shaft is always assumed to correspond to the X-axis" @@ -62,20 +64,23 @@ class Shaft: result += self.segments[i].length return result - def addSegment(self, l, d): + def addSegment(self, l, d, di): #print "Adding segment: ", l, " : ", d - self.segments.append(ShaftSegment(l,d)) - self.sketch.addSegment(l, d) + self.segments.append(ShaftSegment(l,d,di)) + self.sketch.addSegment(l, d, di) # We don't call equilibrium() here because the new segment has no loads defined yet - def updateSegment(self, index, length = None, diameter = None): + def updateSegment(self, index, length = None, diameter = None, innerdiameter = None): oldLength = self.segments[index].length #print "Old length of ", index, ": ", oldLength, ", new Length: ", length, " diameter: ", diameter if length is not None: self.segments[index].length = length if diameter is not None: self.segments[index].diameter = diameter - self.sketch.updateSegment(index, oldLength, self.segments[index].length, self.segments[index].diameter) + if innerdiameter is not None: + self.segments[index].innerdiameter = innerdiameter + self.sketch.updateSegment(index, oldLength, self.segments[index].length, + self.segments[index].diameter, self.segments[index].innerdiameter) self.equilibrium() self.updateDiagrams() diff --git a/src/Mod/PartDesign/WizardShaft/ShaftFeature.py b/src/Mod/PartDesign/WizardShaft/ShaftFeature.py index beac8705e..8cb606351 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftFeature.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftFeature.py @@ -42,7 +42,7 @@ class ShaftFeature: self.sketch = self.Doc.addObject("Sketcher::SketchObject","SketchShaft") self.sketch.Placement = self.App.Placement(self.App.Vector(0,0,0),self.App.Rotation(0,0,0,1)) - def addSegment(self, length, diameter): + def addSegment(self, length, diameter, innerdiameter): "Add a segment at the end of the shaft" # Find constraint indices of vertical line constraint, horizontal line constraint # FIXME: Should have a unique id instead of indices that might change with user editing @@ -53,6 +53,7 @@ class ShaftFeature: # etc. etc. constrRadius = 4 + self.segments * 6 constrLength = 7 + self.segments * 6 + constrInnerRadius = 1 + self.segments * 6 # Find line index of vertical segment, horizontal segment, last shaft segment # FIXME: Should have a unique id instead of indices that might change with user editing segRadius = 1 + self.segments * 2 @@ -62,6 +63,7 @@ class ShaftFeature: segEnd = prevSegEnd + 2 radius = diameter / 2 + innerradius = innerdiameter / 2 oldLength = self.totalLength self.totalLength += length self.segments += 1 @@ -70,13 +72,13 @@ class ShaftFeature: # First segment of shaft # Create centerline self.sketch.addGeometry(Part.Line(self.App.Vector(0,0,0), self.App.Vector(self.totalLength,0,0))) - self.sketch.addConstraint(Sketcher.Constraint('DistanceX',0, self.totalLength)) # Constraint1 - self.sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,1,-1)) # Constraint2 + self.sketch.addConstraint(Sketcher.Constraint('DistanceX',0, self.totalLength)) # Constraint1 + self.sketch.addConstraint(Sketcher.Constraint('DistanceY', -1,1,0,1,innerradius)) # Constraint2 self.sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,1,-2)) # Constraint3 self.sketch.addConstraint(Sketcher.Constraint('Horizontal', 0)) # Constraint4 # Create first segment - self.sketch.addGeometry(Part.Line(self.App.Vector(0,0,0), self.App.Vector(0,radius,0))) - self.sketch.addConstraint(Sketcher.Constraint('DistanceY',1,radius)) # Constraint5 + self.sketch.addGeometry(Part.Line(self.App.Vector(0,innerradius,0), self.App.Vector(0,radius,0))) + self.sketch.addConstraint(Sketcher.Constraint('DistanceY',-1,1,1,2,radius)) # Constraint5 self.sketch.addConstraint(Sketcher.Constraint('Coincident',0,1,1,1)) # Constraint6 self.sketch.addConstraint(Sketcher.Constraint('Vertical',1)) # Constraint7 self.sketch.addGeometry(Part.Line(self.App.Vector(0,radius,0), self.App.Vector(length,radius,0))) @@ -91,7 +93,7 @@ class ShaftFeature: self.sketch.setDatum(0,self.totalLength) # Add segment at the end self.sketch.addGeometry(Part.Line(self.App.Vector(oldLength,self.lastRadius,0), self.App.Vector(oldLength,radius,0))) - self.sketch.addConstraint(Sketcher.Constraint('DistanceY', 0, 1, segRadius, 2, radius)) + self.sketch.addConstraint(Sketcher.Constraint('DistanceY', -1,1, segRadius, 2, radius)) self.sketch.addConstraint(Sketcher.Constraint('Coincident',segRadius,1,prevSegLength,2)) self.sketch.addConstraint(Sketcher.Constraint('Vertical',segRadius)) self.sketch.addGeometry(Part.Line(self.App.Vector(oldLength,radius,0), self.App.Vector(oldLength+length,radius,0))) @@ -100,10 +102,10 @@ class ShaftFeature: self.sketch.addConstraint(Sketcher.Constraint('Horizontal',segLength)) # close the sketch - self.sketch.addGeometry(Part.Line(self.App.Vector(oldLength+length,radius,0), self.App.Vector(oldLength+length,0,0))) + self.sketch.addGeometry(Part.Line(self.App.Vector(oldLength+length,radius,0), self.App.Vector(oldLength+length,innerradius,0))) self.sketch.addConstraint(Sketcher.Constraint('Coincident',0,2,segEnd,2)) self.sketch.addConstraint(Sketcher.Constraint('Coincident',segEnd,1,segLength,2)) - lastRadius = radius + self.lastRadius = radius if oldLength == 0: # create feature @@ -118,16 +120,20 @@ class ShaftFeature: # FIXME: Will give a warning in the console if the active window is not the feature self.Gui.SendMsgToActiveView("ViewFit") - def updateSegment(self, segment, oldLength, length, diameter): + def updateSegment(self, segment, oldLength, length, diameter, innerdiameter): constrRadius = 4 + segment * 6 constrLength = 7 + segment * 6 + constrInnerRadius = 1 # Currently we don't allow multiple different innner diameters # update total length self.totalLength = self.totalLength - oldLength + length # Adjust length of centerline self.sketch.setDatum(0,self.totalLength) # Adjust segment length self.sketch.setDatum(constrLength, length) + # Adjust diameter self.sketch.setDatum(constrRadius, diameter/2) + # Adjust inner diameter + self.sketch.setDatum(constrInnerRadius, innerdiameter/2) # Update feature self.Doc.recompute() self.Gui.SendMsgToActiveView("ViewFit") diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py index a27289e20..412052175 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py @@ -30,17 +30,19 @@ class WizardShaftTable: rowDict = { "Length" : 0, "Diameter" : 1, - "LoadType" : 2, - "LoadSize" : 3, - "LoadLocation" : 4, - "StartEdgeType" : 5, - "StartEdgeSize" : 6, - "EndEdgeType" : 7, - "EndEdgeSize" : 8 + "InnerDiameter" : 2, + "LoadType" : 3, + "LoadSize" : 4, + "LoadLocation" : 5, + "StartEdgeType" : 6, + "StartEdgeSize" : 7, + "EndEdgeType" : 8, + "EndEdgeSize" : 9 } rowDictReverse = {} headers = ["Length [mm]", "Diameter [mm]", + "Inner diameter [mm]", "Load type", "Load [N]", "Location [mm]", @@ -61,7 +63,7 @@ class WizardShaftTable: self.shaft = s # Create table widget self.widget = QtGui.QTableWidget(len(self.rowDict), 0) - self.widget.resize(QtCore.QSize(300,100)) + self.widget.resize(QtCore.QSize(300,200)) #self.widget.setFocusPolicy(QtCore.Qt.StrongFocus) # Label rows and columns @@ -102,16 +104,18 @@ class WizardShaftTable: index = self.widget.columnCount() # Make an intelligent guess at the length/dia of the next segment if index > 0: - length = self.shaft.segments[index-1].length + length = self.shaft.segments[index-1].length diameter = self.shaft.segments[index-1].diameter if index > 2: diameter -= 5.0 else: diameter += 5.0 + innerdiameter = self.shaft.segments[index-1].innerdiameter else: length = 20.0 diameter = 10.0 - self.shaft.addSegment(length, diameter) + innerdiameter = 0.0 + self.shaft.addSegment(length, diameter, innerdiameter) self.widget.insertColumn(index) self.widget.setHorizontalHeaderItem(index + 1, QtGui.QTableWidgetItem("Section %s" % (index + 1))) @@ -132,6 +136,14 @@ class WizardShaftTable: widget.setValue(diameter) widget.valueChanged.connect(self.slotValueChanged) widget.editingFinished.connect(self.slotEditingFinished) + # inner Diameter + widget = QtGui.QDoubleSpinBox(self.widget) + widget.setMinimum(0) + widget.setMaximum(1E9) + self.widget.setCellWidget(self.rowDict["InnerDiameter"], index, widget) + widget.setValue(innerdiameter) + widget.valueChanged.connect(self.slotValueChanged) + widget.editingFinished.connect(self.slotEditingFinished) # Load type widget = QtGui.QComboBox(self.widget) widget.insertItem(0, "None") @@ -203,6 +215,8 @@ class WizardShaftTable: self.shaft.updateSegment(self.editedColumn, length = self.getDoubleValue(rowName, self.editedColumn)) elif rowName == "Diameter": self.shaft.updateSegment(self.editedColumn, diameter = self.getDoubleValue(rowName, self.editedColumn)) + elif rowName == "InnerDiameter": + self.shaft.updateSegment(self.editedColumn, innerdiameter = self.getDoubleValue(rowName, self.editedColumn)) elif rowName == "LoadType": self.shaft.updateLoad(self.editedColumn, loadType = self.getListValue(rowName, self.editedColumn)) elif rowName == "LoadSize": @@ -232,6 +246,13 @@ class WizardShaftTable: def getDiameter(self, column): return self.getDoubleValue("Diameter", column) + def setInnerDiameter(self, column, d): + self.setDoubleValue("InnerDiameter", column, d) + self.shaft.updateSegment(column, innerdiameter = d) + + def getInnerDiameter(self, column): + return self.getDoubleValue("InnerDiameter", column) + @QtCore.pyqtSlot('QString') def slotLoadType(self, text): if text != "Fixed": From ea1ae29985597231f790f4e6473debc92d9a2188 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 18:53:47 +0430 Subject: [PATCH 03/15] WizardShaft: Automatic adjustment of axes length --- src/Mod/PartDesign/WizardShaft/ShaftDiagram.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py index 01e12b5a5..097b571c0 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py @@ -73,7 +73,7 @@ class Diagram: if xlength is not None: self.xlength = xlength # Calculate points - (self.xpoints, self.ypoints) = self.function.evaluate(self.xlength, self.numxpoints) + (self.xpoints, self.ypoints) = self.function.evaluate(self.xlength, self.numxpoints) # Create plot self.plot() @@ -91,3 +91,8 @@ class Diagram: self.xpoints = [p * self.xscale for p in self.xpoints] self.ypoints = [p * self.yscale for p in self.ypoints] self.thePlot.plot(self.xpoints, self.ypoints) + plots = self.thePlot.series + axes = plots[0].axes + axes.set_xlim(right = max(self.xpoints) * 1.05) + axes.set_ylim(min(self.ypoints) * 1.05, max(self.ypoints) * 1.05) + self.thePlot.update() From 0773311d5b160858113d389ed06fca44e682f2be Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Sun, 25 Nov 2012 20:15:01 +0430 Subject: [PATCH 04/15] Created Draft feature for PartDesign --- src/Mod/PartDesign/App/AppPartDesign.cpp | 2 + src/Mod/PartDesign/App/CMakeLists.txt | 2 + src/Mod/PartDesign/App/FeatureDraft.cpp | 283 ++++++ src/Mod/PartDesign/App/FeatureDraft.h | 61 ++ src/Mod/PartDesign/App/Makefile.am | 2 + src/Mod/PartDesign/Gui/AppPartDesignGui.cpp | 2 + src/Mod/PartDesign/Gui/CMakeLists.txt | 7 + src/Mod/PartDesign/Gui/Command.cpp | 862 ++++++++++-------- src/Mod/PartDesign/Gui/Makefile.am | 7 + .../Gui/Resources/icons/PartDesign_Draft.svg | 0 .../PartDesign/Gui/TaskDraftParameters.cpp | 432 +++++++++ src/Mod/PartDesign/Gui/TaskDraftParameters.h | 126 +++ src/Mod/PartDesign/Gui/TaskDraftParameters.ui | 120 +++ src/Mod/PartDesign/Gui/ViewProviderDraft.cpp | 130 +++ src/Mod/PartDesign/Gui/ViewProviderDraft.h | 58 ++ src/Mod/PartDesign/Gui/Workbench.cpp | 4 + 16 files changed, 1722 insertions(+), 376 deletions(-) create mode 100644 src/Mod/PartDesign/App/FeatureDraft.cpp create mode 100644 src/Mod/PartDesign/App/FeatureDraft.h create mode 100644 src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg create mode 100644 src/Mod/PartDesign/Gui/TaskDraftParameters.cpp create mode 100644 src/Mod/PartDesign/Gui/TaskDraftParameters.h create mode 100644 src/Mod/PartDesign/Gui/TaskDraftParameters.ui create mode 100644 src/Mod/PartDesign/Gui/ViewProviderDraft.cpp create mode 100644 src/Mod/PartDesign/Gui/ViewProviderDraft.h diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index 1f9b8136b..ace223d46 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -38,6 +38,7 @@ #include "Body.h" #include "FeatureDressUp.h" #include "FeatureChamfer.h" +#include "FeatureDraft.h" #include "FeatureFace.h" #include "FeatureSubtractive.h" #include "FeatureAdditive.h" @@ -96,6 +97,7 @@ void PartDesignExport initPartDesign() PartDesign::Groove ::init(); PartDesign::Chamfer ::init(); PartDesign::Face ::init(); + PartDesign::Draft ::init(); } } // extern "C" diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index 1a3cf0f2e..359349364 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -51,6 +51,8 @@ SET(FeaturesDressUp_SRCS FeatureFillet.h FeatureChamfer.cpp FeatureChamfer.h + FeatureDraft.cpp + FeatureDraft.h ) SOURCE_GROUP("DressUpFeatures" FILES ${FeaturesDressUp_SRCS}) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp new file mode 100644 index 000000000..16dfb3f59 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -0,0 +1,283 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +//# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include "FeatureDraft.h" + + +using namespace PartDesign; + +#include + + +PROPERTY_SOURCE(PartDesign::Draft, PartDesign::DressUp) + +const App::PropertyFloatConstraint::Constraints floatAngle = {0.0f,89.99f,0.1f}; + +Draft::Draft() +{ + ADD_PROPERTY(Angle,(1.5f)); + Angle.setConstraints(&floatAngle); + ADD_PROPERTY_TYPE(NeutralPlane,(0),"Draft",(App::PropertyType)(App::Prop_None),"NeutralPlane"); + ADD_PROPERTY_TYPE(PullDirection,(0),"Draft",(App::PropertyType)(App::Prop_None),"PullDirection"); + ADD_PROPERTY(Reversed,(0)); +} + +short Draft::mustExecute() const +{ + if (Placement.isTouched() || + Angle.isTouched() || + NeutralPlane.isTouched() || + PullDirection.isTouched() || + Reversed.isTouched()) + return 1; + return DressUp::mustExecute(); +} + +App::DocumentObjectExecReturn *Draft::execute(void) +{ + // Get parameters + // Base shape + App::DocumentObject* link = Base.getValue(); + if (!link) + return new App::DocumentObjectExecReturn("No object linked"); + if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("Linked object is not a Part object"); + Part::Feature *base = static_cast(Base.getValue()); + const Part::TopoShape& TopShape = base->Shape.getShape(); + if (TopShape._Shape.IsNull()) + return new App::DocumentObjectExecReturn("Cannot draft invalid shape"); + + // Faces where draft should be applied + const std::vector& SubVals = Base.getSubValuesStartsWith("Face"); + if (SubVals.size() == 0) + return new App::DocumentObjectExecReturn("No faces specified"); + + // Draft angle + float angle = Angle.getValue(); + if ((angle < 0.0) || (angle >= 90.0)) + return new App::DocumentObjectExecReturn("Draft angle must be between 0 and 90 degrees"); + angle = angle / 180.0 * M_PI; + + // Pull direction + gp_Dir pullDirection; + App::DocumentObject* refDirection = PullDirection.getValue(); + if (refDirection != NULL) { + if (!refDirection->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + throw Base::Exception("Pull direction reference must be an edge of a feature"); + std::vector subStrings = PullDirection.getSubValues(); + if (subStrings.empty() || subStrings[0].empty()) + throw Base::Exception("No pull direction reference specified"); + + Part::Feature* refFeature = static_cast(refDirection); + Part::TopoShape refShape = refFeature->Shape.getShape(); + TopoDS_Shape ref = refShape.getSubShape(subStrings[0].c_str()); + + if (ref.ShapeType() == TopAbs_EDGE) { + TopoDS_Edge refEdge = TopoDS::Edge(ref); + if (refEdge.IsNull()) + throw Base::Exception("Failed to extract pull direction reference edge"); + BRepAdaptor_Curve adapt(refEdge); + if (adapt.GetType() != GeomAbs_Line) + throw Base::Exception("Pull direction reference edge must be linear"); + + pullDirection = adapt.Line().Direction(); + } else { + throw Base::Exception("Pull direction reference must be an edge"); + } + + TopLoc_Location invObjLoc = this->getLocation().Inverted(); + pullDirection.Transform(invObjLoc.Transformation()); + } + + // Neutral plane + gp_Pln neutralPlane; + App::DocumentObject* refPlane = NeutralPlane.getValue(); + if (refPlane == NULL) { + // Try to guess a neutral plane from the first selected face + // Get edges of first selected face + TopoDS_Shape face = TopShape.getSubShape(SubVals[0].c_str()); + TopTools_IndexedMapOfShape mapOfEdges; + TopExp::MapShapes(face, TopAbs_EDGE, mapOfEdges); + bool found; + + for (int i = 1; i <= mapOfEdges.Extent(); i++) { + // Note: What happens if mapOfEdges(i) is the degenerated edge of a cone? + // But in that case the draft is not possible anyway! + BRepAdaptor_Curve c(TopoDS::Edge(mapOfEdges(i))); + gp_Pnt p1 = c.Value(c.FirstParameter()); + gp_Pnt p2 = c.Value(c.LastParameter()); + + if (c.IsClosed()) { + // Edge is a circle or a circular arc (other types are not allowed for drafting) + neutralPlane = gp_Pln(p1, c.Circle().Axis().Direction()); + found = true; + break; + } else { + // Edge is linear + // Find midpoint of edge and create auxiliary plane through midpoint normal to edge + gp_Pnt pm = c.Value((c.FirstParameter() + c.LastParameter()) / 2.0); + Geom_Plane aux(pm, gp_Dir(p2.X() - p1.X(), p2.Y() - p1.Y(), p2.Z() - p1.Z())); + // Intersect plane with face. Is there no easier way? + BRepAdaptor_Surface adapt(TopoDS::Face(face), Standard_False); + Handle_Geom_Surface sf = adapt.Surface().Surface(); + //Handle_Geom_Surface auxsf(&aux); + GeomAPI_IntSS intersector(&aux, sf, Precision::Confusion()); + if (!intersector.IsDone()) + continue; + Handle_Geom_Curve icurve = intersector.Line(1); + if (!icurve->IsKind(STANDARD_TYPE(Geom_Line))) + continue; + // TODO: How to extract the line from icurve without creating an edge first? + TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(icurve); + BRepAdaptor_Curve c(edge); + neutralPlane = gp_Pln(pm, c.Line().Direction()); + found = true; + break; + } + } + + if (!found) + throw Base::Exception("No neutral plane specified and none can be guessed"); + } else { + if (!refPlane->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + throw Base::Exception("Neutral plane reference must be face of a feature"); + std::vector subStrings = NeutralPlane.getSubValues(); + if (subStrings.empty() || subStrings[0].empty()) + throw Base::Exception("No neutral plane reference specified"); + + Part::Feature* refFeature = static_cast(refPlane); + Part::TopoShape refShape = refFeature->Shape.getShape(); + TopoDS_Shape ref = refShape.getSubShape(subStrings[0].c_str()); + + if (ref.ShapeType() == TopAbs_FACE) { + TopoDS_Face refFace = TopoDS::Face(ref); + if (refFace.IsNull()) + throw Base::Exception("Failed to extract neutral plane reference face"); + BRepAdaptor_Surface adapt(refFace); + if (adapt.GetType() != GeomAbs_Plane) + throw Base::Exception("Neutral plane reference face must be planar"); + + neutralPlane = adapt.Plane(); + } else if (ref.ShapeType() == TopAbs_EDGE) { + if (refDirection != NULL) { + // Create neutral plane through edge normal to pull direction + TopoDS_Edge refEdge = TopoDS::Edge(ref); + if (refEdge.IsNull()) + throw Base::Exception("Failed to extract neutral plane reference edge"); + BRepAdaptor_Curve c(refEdge); + if (!c.GetType() == GeomAbs_Line) + throw Base::Exception("Neutral plane reference edge must be linear"); + double a = c.Line().Angle(gp_Lin(c.Value(c.FirstParameter()), pullDirection)); + if (std::fabs(a - M_PI_2) > Precision::Confusion()) + throw Base::Exception("Neutral plane reference edge must be normal to pull direction"); + neutralPlane = gp_Pln(c.Value(c.FirstParameter()), pullDirection); + } else { + throw Base::Exception("Neutral plane reference can only be an edge if pull direction is defined"); + } + } else { + throw Base::Exception("Neutral plane reference must be a face"); + } + + TopLoc_Location invObjLoc = this->getLocation().Inverted(); + neutralPlane.Transform(invObjLoc.Transformation()); + } + + if (refDirection == NULL) { + // Choose pull direction normal to neutral plane + pullDirection = neutralPlane.Axis().Direction(); + } + + // Reversed pull direction + bool reversed = Reversed.getValue(); + if (reversed) + angle *= -1.0; + + this->positionByBase(); + // create an untransformed copy of the base shape + Part::TopoShape baseShape(TopShape); + baseShape.setTransform(Base::Matrix4D()); + try { + BRepOffsetAPI_DraftAngle mkDraft(baseShape._Shape); + // Note: + // LocOpe_SplitDrafts can split a face with a wire and apply draft to both parts + // Not clear though whether the face must have free boundaries + // LocOpe_DPrism can create a stand-alone draft prism. The sketch can only have a single + // wire, though. + // BRepFeat_MakeDPrism requires a support for the operation but will probably support multiple + // wires in the sketch + + for (std::vector::const_iterator it=SubVals.begin(); it != SubVals.end(); ++it) { + TopoDS_Face face = TopoDS::Face(baseShape.getSubShape(it->c_str())); + // TODO: What is the flag for? + mkDraft.Add(face, pullDirection, angle, neutralPlane); + if (!mkDraft.AddDone()) { + // Note: the function ProblematicShape returns the face on which the error occurred, + mkDraft.Remove(face); + Base::Console().Error("Adding face failed: %u\n", mkDraft.Status()); + } + } + + mkDraft.Build(); + if (!mkDraft.IsDone()) + return new App::DocumentObjectExecReturn("Failed to create draft"); + + TopoDS_Shape shape = mkDraft.Shape(); + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is null"); + + this->Shape.setValue(shape); + return App::DocumentObject::StdReturn; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); + } +} diff --git a/src/Mod/PartDesign/App/FeatureDraft.h b/src/Mod/PartDesign/App/FeatureDraft.h new file mode 100644 index 000000000..0b98c89b2 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureDraft.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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 PARTDESIGN_FEATUREDRAFT_H +#define PARTDESIGN_FEATUREDRAFT_H + +#include +#include +#include "FeatureDressUp.h" + +namespace PartDesign +{ + +class PartDesignExport Draft : public DressUp +{ + PROPERTY_HEADER(PartDesign::Draft); + +public: + Draft(); + + App::PropertyFloatConstraint Angle; + App::PropertyLinkSub NeutralPlane; + App::PropertyLinkSub PullDirection; + App::PropertyBool Reversed; + + /** @name methods override feature */ + //@{ + /// recalculate the feature + App::DocumentObjectExecReturn *execute(void); + short mustExecute() const; + /// returns the type name of the view provider + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderDraft"; + } + //@} +}; + +} //namespace PartDesign + + +#endif // PARTDESIGN_FEATUREDRAFT_H diff --git a/src/Mod/PartDesign/App/Makefile.am b/src/Mod/PartDesign/App/Makefile.am index 62dcad923..cade8f97c 100644 --- a/src/Mod/PartDesign/App/Makefile.am +++ b/src/Mod/PartDesign/App/Makefile.am @@ -15,6 +15,8 @@ libPartDesign_la_SOURCES=\ FeaturePocket.h \ FeatureChamfer.cpp \ FeatureChamfer.h \ + FeatureDraft.cpp \ + FeatureDraft.h \ FeatureDressUp.cpp \ FeatureDressUp.h \ FeatureGroove.cpp \ diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp index a65a57e7f..fc08cc8c0 100644 --- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp +++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp @@ -36,6 +36,7 @@ #include "ViewProviderPad.h" #include "ViewProviderChamfer.h" #include "ViewProviderFillet.h" +#include "ViewProviderDraft.h" #include "ViewProviderRevolution.h" #include "ViewProviderGroove.h" #include "ViewProviderMirrored.h" @@ -92,6 +93,7 @@ void PartDesignGuiExport initPartDesignGui() PartDesignGui::ViewProviderGroove ::init(); PartDesignGui::ViewProviderChamfer ::init(); PartDesignGui::ViewProviderFillet ::init(); + PartDesignGui::ViewProviderDraft ::init(); PartDesignGui::ViewProviderMirrored ::init(); PartDesignGui::ViewProviderLinearPattern ::init(); PartDesignGui::ViewProviderPolarPattern ::init(); diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 0d603218e..fb88806e6 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -31,6 +31,7 @@ set(PartDesignGui_MOC_HDRS TaskPocketParameters.h TaskChamferParameters.h TaskFilletParameters.h + TaskDraftParameters.h TaskHoleParameters.h TaskRevolutionParameters.h TaskGrooveParameters.h @@ -53,6 +54,7 @@ set(PartDesignGui_UIC_SRCS TaskPocketParameters.ui TaskChamferParameters.ui TaskFilletParameters.ui + TaskDraftParameters.ui TaskHoleParameters.ui TaskRevolutionParameters.ui TaskGrooveParameters.ui @@ -78,6 +80,8 @@ SET(PartDesignGuiViewProvider_SRCS ViewProviderChamfer.h ViewProviderFillet.cpp ViewProviderFillet.h + ViewProviderDraft.cpp + ViewProviderDraft.h ViewProviderRevolution.cpp ViewProviderRevolution.h ViewProviderGroove.cpp @@ -115,6 +119,9 @@ SET(PartDesignGuiTaskDlgs_SRCS TaskFilletParameters.ui TaskFilletParameters.cpp TaskFilletParameters.h + TaskDraftParameters.ui + TaskDraftParameters.cpp + TaskDraftParameters.h TaskRevolutionParameters.ui TaskRevolutionParameters.cpp TaskRevolutionParameters.h diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 1145dd8e6..700a6dda7 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -20,40 +20,41 @@ * * ***************************************************************************/ - -#include "PreCompiled.h" -#ifndef _PreComp_ -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include #include #include - -using namespace std; -#include "FeaturePickDialog.h" - +using namespace std; + +#include "FeaturePickDialog.h" + namespace Gui { //=========================================================================== // Common utility functions @@ -62,7 +63,7 @@ namespace Gui { // Take a list of Part2DObjects and erase those which are not eligible for creating a // SketchBased feature. If supportRequired is true, also erase those that cannot be used to define // a Subtractive feature -void validateSketches(std::vector& sketches, const bool supportRequired) +void validateSketches(std::vector& sketches, const bool supportRequired) { std::vector::iterator s = sketches.begin(); @@ -448,130 +449,130 @@ CmdPartDesignFillet::CmdPartDesignFillet() sStatusTip = sToolTipText; sPixmap = "PartDesign_Fillet"; } - -void CmdPartDesignFillet::activated(int iMsg) -{ - std::vector selection = getSelection().getSelectionEx(); - - if (selection.size() != 1) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select an edge, face or body. Only one body is allowed.")); - return; - } - - if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){ - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"), - QObject::tr("Fillet works only on parts")); - return; - } - - Part::Feature *base = static_cast(selection[0].getObject()); - - const Part::TopoShape& TopShape = base->Shape.getShape(); - if (TopShape._Shape.IsNull()){ - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Shape of selected Part is empty")); - return; - } - - TopTools_IndexedMapOfShape mapOfEdges; - TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; - TopExp::MapShapesAndAncestors(TopShape._Shape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); - TopExp::MapShapes(TopShape._Shape, TopAbs_EDGE, mapOfEdges); - - std::vector SubNames = std::vector(selection[0].getSubNames()); - - int i = 0; - - while(i < SubNames.size()) - { - std::string aSubName = static_cast(SubNames.at(i)); - - if (aSubName.size() > 4 && aSubName.substr(0,4) == "Edge") { - TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(aSubName.c_str())); - const TopTools_ListOfShape& los = mapEdgeFace.FindFromKey(edge); - - if(los.Extent() != 2) - { - SubNames.erase(SubNames.begin()+i); - continue; - } - - const TopoDS_Shape& face1 = los.First(); - const TopoDS_Shape& face2 = los.Last(); - GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(edge), - TopoDS::Face(face1), - TopoDS::Face(face2)); - if (cont != GeomAbs_C0) { - SubNames.erase(SubNames.begin()+i); - continue; - } - - i++; - } - else if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") { - TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str())); - - TopTools_IndexedMapOfShape mapOfFaces; - TopExp::MapShapes(face, TopAbs_EDGE, mapOfFaces); - - for(int j = 1; j <= mapOfFaces.Extent(); ++j) { - TopoDS_Edge edge = TopoDS::Edge(mapOfFaces.FindKey(j)); - - int id = mapOfEdges.FindIndex(edge); - - std::stringstream buf; - buf << "Edge"; - buf << id; - - if(std::find(SubNames.begin(),SubNames.end(),buf.str()) == SubNames.end()) - { - SubNames.push_back(buf.str()); - } - - } - - SubNames.erase(SubNames.begin()+i); - } - // empty name or any other sub-element - else { - SubNames.erase(SubNames.begin()+i); - } - } - - if (SubNames.size() == 0) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No fillet possible on selected faces/edges")); - return; - } - - std::string SelString; - SelString += "(App."; - SelString += "ActiveDocument";//getObject()->getDocument()->getName(); - SelString += "."; - SelString += selection[0].getFeatName(); - SelString += ",["; - for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ - SelString += "\""; - SelString += *it; - SelString += "\""; - if(it != --SubNames.end()) - SelString += ","; - } - SelString += "])"; - - std::string FeatName = getUniqueObjectName("Fillet"); - - openCommand("Make Fillet"); - doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Fillet\",\"%s\")",FeatName.c_str()); - doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); - doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",selection[0].getFeatName()); - doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); - - copyVisual(FeatName.c_str(), "ShapeColor", selection[0].getFeatName()); - copyVisual(FeatName.c_str(), "LineColor", selection[0].getFeatName()); - copyVisual(FeatName.c_str(), "PointColor", selection[0].getFeatName()); -} + +void CmdPartDesignFillet::activated(int iMsg) +{ + std::vector selection = getSelection().getSelectionEx(); + + if (selection.size() != 1) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select an edge, face or body. Only one body is allowed.")); + return; + } + + if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"), + QObject::tr("Fillet works only on parts")); + return; + } + + Part::Feature *base = static_cast(selection[0].getObject()); + + const Part::TopoShape& TopShape = base->Shape.getShape(); + if (TopShape._Shape.IsNull()){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Shape of selected Part is empty")); + return; + } + + TopTools_IndexedMapOfShape mapOfEdges; + TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; + TopExp::MapShapesAndAncestors(TopShape._Shape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); + TopExp::MapShapes(TopShape._Shape, TopAbs_EDGE, mapOfEdges); + + std::vector SubNames = std::vector(selection[0].getSubNames()); + + int i = 0; + + while(i < SubNames.size()) + { + std::string aSubName = static_cast(SubNames.at(i)); + + if (aSubName.size() > 4 && aSubName.substr(0,4) == "Edge") { + TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(aSubName.c_str())); + const TopTools_ListOfShape& los = mapEdgeFace.FindFromKey(edge); + + if(los.Extent() != 2) + { + SubNames.erase(SubNames.begin()+i); + continue; + } + + const TopoDS_Shape& face1 = los.First(); + const TopoDS_Shape& face2 = los.Last(); + GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(edge), + TopoDS::Face(face1), + TopoDS::Face(face2)); + if (cont != GeomAbs_C0) { + SubNames.erase(SubNames.begin()+i); + continue; + } + + i++; + } + else if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") { + TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str())); + + TopTools_IndexedMapOfShape mapOfFaces; + TopExp::MapShapes(face, TopAbs_EDGE, mapOfFaces); + + for(int j = 1; j <= mapOfFaces.Extent(); ++j) { + TopoDS_Edge edge = TopoDS::Edge(mapOfFaces.FindKey(j)); + + int id = mapOfEdges.FindIndex(edge); + + std::stringstream buf; + buf << "Edge"; + buf << id; + + if(std::find(SubNames.begin(),SubNames.end(),buf.str()) == SubNames.end()) + { + SubNames.push_back(buf.str()); + } + + } + + SubNames.erase(SubNames.begin()+i); + } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } + } + + if (SubNames.size() == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("No fillet possible on selected faces/edges")); + return; + } + + std::string SelString; + SelString += "(App."; + SelString += "ActiveDocument";//getObject()->getDocument()->getName(); + SelString += "."; + SelString += selection[0].getFeatName(); + SelString += ",["; + for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ + SelString += "\""; + SelString += *it; + SelString += "\""; + if(it != --SubNames.end()) + SelString += ","; + } + SelString += "])"; + + std::string FeatName = getUniqueObjectName("Fillet"); + + openCommand("Make Fillet"); + doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Fillet\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); + doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",selection[0].getFeatName()); + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); + + copyVisual(FeatName.c_str(), "ShapeColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "LineColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "PointColor", selection[0].getFeatName()); +} bool CmdPartDesignFillet::isActive(void) { @@ -594,137 +595,245 @@ CmdPartDesignChamfer::CmdPartDesignChamfer() sStatusTip = sToolTipText; sPixmap = "PartDesign_Chamfer"; } - -void CmdPartDesignChamfer::activated(int iMsg) -{ - std::vector selection = getSelection().getSelectionEx(); - - if (selection.size() != 1) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select an edge, face or body. Only one body is allowed.")); - return; - } - - if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){ - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"), - QObject::tr("Chamfer works only on parts")); - return; - } - - Part::Feature *base = static_cast(selection[0].getObject()); - - const Part::TopoShape& TopShape = base->Shape.getShape(); - - if (TopShape._Shape.IsNull()){ - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Shape of selected part is empty")); - return; - } - - TopTools_IndexedMapOfShape mapOfEdges; - TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; - TopExp::MapShapesAndAncestors(TopShape._Shape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); - TopExp::MapShapes(TopShape._Shape, TopAbs_EDGE, mapOfEdges); - - std::vector SubNames = std::vector(selection[0].getSubNames()); - - int i = 0; - - while(i < SubNames.size()) - { - std::string aSubName = static_cast(SubNames.at(i)); - - if (aSubName.size() > 4 && aSubName.substr(0,4) == "Edge") { - TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(aSubName.c_str())); - const TopTools_ListOfShape& los = mapEdgeFace.FindFromKey(edge); - - if(los.Extent() != 2) - { - SubNames.erase(SubNames.begin()+i); - continue; - } - - const TopoDS_Shape& face1 = los.First(); - const TopoDS_Shape& face2 = los.Last(); - GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(edge), - TopoDS::Face(face1), - TopoDS::Face(face2)); - if (cont != GeomAbs_C0) { - SubNames.erase(SubNames.begin()+i); - continue; - } - - i++; - } - else if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") { - TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str())); - - TopTools_IndexedMapOfShape mapOfFaces; - TopExp::MapShapes(face, TopAbs_EDGE, mapOfFaces); - - for(int j = 1; j <= mapOfFaces.Extent(); ++j) { - TopoDS_Edge edge = TopoDS::Edge(mapOfFaces.FindKey(j)); - - int id = mapOfEdges.FindIndex(edge); - - std::stringstream buf; - buf << "Edge"; - buf << id; - - if(std::find(SubNames.begin(),SubNames.end(),buf.str()) == SubNames.end()) - { - SubNames.push_back(buf.str()); - } - - } - - SubNames.erase(SubNames.begin()+i); - } - // empty name or any other sub-element - else { - SubNames.erase(SubNames.begin()+i); - } - } - - if (SubNames.size() == 0) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No chamfer possible on selected faces/edges")); - return; - } - - std::string SelString; - SelString += "(App."; - SelString += "ActiveDocument";//getObject()->getDocument()->getName(); - SelString += "."; - SelString += selection[0].getFeatName(); - SelString += ",["; - for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ - SelString += "\""; - SelString += *it; - SelString += "\""; - if(it != --SubNames.end()) - SelString += ","; - } - SelString += "])"; - - std::string FeatName = getUniqueObjectName("Chamfer"); - - openCommand("Make Chamfer"); - doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Chamfer\",\"%s\")",FeatName.c_str()); - doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); - doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",selection[0].getFeatName()); - doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); - - copyVisual(FeatName.c_str(), "ShapeColor", selection[0].getFeatName()); - copyVisual(FeatName.c_str(), "LineColor", selection[0].getFeatName()); - copyVisual(FeatName.c_str(), "PointColor", selection[0].getFeatName()); -} + +void CmdPartDesignChamfer::activated(int iMsg) +{ + std::vector selection = getSelection().getSelectionEx(); + + if (selection.size() != 1) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select an edge, face or body. Only one body is allowed.")); + return; + } + + if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"), + QObject::tr("Chamfer works only on parts")); + return; + } + + Part::Feature *base = static_cast(selection[0].getObject()); + + const Part::TopoShape& TopShape = base->Shape.getShape(); + + if (TopShape._Shape.IsNull()){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Shape of selected part is empty")); + return; + } + + TopTools_IndexedMapOfShape mapOfEdges; + TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; + TopExp::MapShapesAndAncestors(TopShape._Shape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); + TopExp::MapShapes(TopShape._Shape, TopAbs_EDGE, mapOfEdges); + + std::vector SubNames = std::vector(selection[0].getSubNames()); + + int i = 0; + + while(i < SubNames.size()) + { + std::string aSubName = static_cast(SubNames.at(i)); + + if (aSubName.size() > 4 && aSubName.substr(0,4) == "Edge") { + TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(aSubName.c_str())); + const TopTools_ListOfShape& los = mapEdgeFace.FindFromKey(edge); + + if(los.Extent() != 2) + { + SubNames.erase(SubNames.begin()+i); + continue; + } + + const TopoDS_Shape& face1 = los.First(); + const TopoDS_Shape& face2 = los.Last(); + GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(edge), + TopoDS::Face(face1), + TopoDS::Face(face2)); + if (cont != GeomAbs_C0) { + SubNames.erase(SubNames.begin()+i); + continue; + } + + i++; + } + else if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") { + TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str())); + + TopTools_IndexedMapOfShape mapOfFaces; + TopExp::MapShapes(face, TopAbs_EDGE, mapOfFaces); + + for(int j = 1; j <= mapOfFaces.Extent(); ++j) { + TopoDS_Edge edge = TopoDS::Edge(mapOfFaces.FindKey(j)); + + int id = mapOfEdges.FindIndex(edge); + + std::stringstream buf; + buf << "Edge"; + buf << id; + + if(std::find(SubNames.begin(),SubNames.end(),buf.str()) == SubNames.end()) + { + SubNames.push_back(buf.str()); + } + + } + + SubNames.erase(SubNames.begin()+i); + } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } + } + + if (SubNames.size() == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("No chamfer possible on selected faces/edges")); + return; + } + + std::string SelString; + SelString += "(App."; + SelString += "ActiveDocument";//getObject()->getDocument()->getName(); + SelString += "."; + SelString += selection[0].getFeatName(); + SelString += ",["; + for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ + SelString += "\""; + SelString += *it; + SelString += "\""; + if(it != --SubNames.end()) + SelString += ","; + } + SelString += "])"; + + std::string FeatName = getUniqueObjectName("Chamfer"); + + openCommand("Make Chamfer"); + doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Chamfer\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); + doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",selection[0].getFeatName()); + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); + + copyVisual(FeatName.c_str(), "ShapeColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "LineColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "PointColor", selection[0].getFeatName()); +} bool CmdPartDesignChamfer::isActive(void) { return hasActiveDocument(); } +//=========================================================================== +// PartDesign_Draft +//=========================================================================== +DEF_STD_CMD_A(CmdPartDesignDraft); + +CmdPartDesignDraft::CmdPartDesignDraft() + :Command("PartDesign_Draft") +{ + sAppModule = "PartDesign"; + sGroup = QT_TR_NOOP("PartDesign"); + sMenuText = QT_TR_NOOP("Draft"); + sToolTipText = QT_TR_NOOP("Make a draft on a face"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "PartDesign_Draft"; +} + +void CmdPartDesignDraft::activated(int iMsg) +{ + std::vector selection = getSelection().getSelectionEx(); + + if (selection.size() < 1) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select one or more faces.")); + return; + } + + if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"), + QObject::tr("Draft works only on parts")); + return; + } + + Part::Feature *base = static_cast(selection[0].getObject()); + + const Part::TopoShape& TopShape = base->Shape.getShape(); + if (TopShape._Shape.IsNull()){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Shape of selected Part is empty")); + return; + } + + std::vector SubNames = std::vector(selection[0].getSubNames()); + int i = 0; + + while(i < SubNames.size()) + { + std::string aSubName = static_cast(SubNames.at(i)); + + if(aSubName.size() > 4 && aSubName.substr(0,4) == "Face") { + // Check for valid face types + TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(aSubName.c_str())); + BRepAdaptor_Surface sf(face); + if ((sf.GetType() != GeomAbs_Plane) && (sf.GetType() != GeomAbs_Cylinder) && (sf.GetType() != GeomAbs_Cone)) + SubNames.erase(SubNames.begin()+i); + } else { + // empty name or any other sub-element + SubNames.erase(SubNames.begin()+i); + } + + i++; + } + + if (SubNames.size() == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("No draft possible on selected faces")); + return; + } + + std::string SelString; + SelString += "(App."; + SelString += "ActiveDocument"; + SelString += "."; + SelString += selection[0].getFeatName(); + SelString += ",["; + for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ + SelString += "\""; + SelString += *it; + SelString += "\""; + if(it != --SubNames.end()) + SelString += ","; + } + SelString += "])"; + + std::string FeatName = getUniqueObjectName("Draft"); + + // We don't create any defaults for neutral plane and pull direction, but Draft::execute() + // will choose them. + openCommand("Make Draft"); + doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Draft\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); + doCommand(Doc,"App.activeDocument().%s.Angle = %f",FeatName.c_str(), 1.5); + updateActive(); + if (isActiveObjectValid()) { + doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",selection[0].getFeatName()); + } + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); + + copyVisual(FeatName.c_str(), "ShapeColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "LineColor", selection[0].getFeatName()); + copyVisual(FeatName.c_str(), "PointColor", selection[0].getFeatName()); +} + +bool CmdPartDesignDraft::isActive(void) +{ + return hasActiveDocument(); +} + //=========================================================================== // PartDesign_Mirrored //=========================================================================== @@ -744,36 +853,36 @@ CmdPartDesignMirrored::CmdPartDesignMirrored() void CmdPartDesignMirrored::activated(int iMsg) { - // Get a valid original from the user - // First check selections - std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); - std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); - // Next create a list of all eligible objects - if (features.size() == 0) { - features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); - subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Get a valid original from the user + // First check selections + std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); + std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Next create a list of all eligible objects + if (features.size() == 0) { + features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); + subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); // If there is more than one selected or eligible object, show dialog and let user pick one if (features.size() > 1) { PartDesignGui::FeaturePickDialog Dlg(features); if ((Dlg.exec() != QDialog::Accepted) || (features = Dlg.getFeatures()).empty()) return; // Cancelled or nothing selected - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a subtractive or additive feature first, please")); - return; - } - } + } else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), + QObject::tr("Please create a subtractive or additive feature first, please")); + return; + } + } std::string FeatName = getUniqueObjectName("Mirrored"); std::stringstream str; std::vector tempSelNames; str << "App.activeDocument()." << FeatName << ".Originals = ["; - for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ - str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; - tempSelNames.push_back((*it)->getNameInDocument()); + for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ + str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; + tempSelNames.push_back((*it)->getNameInDocument()); } str << "]"; @@ -818,36 +927,36 @@ CmdPartDesignLinearPattern::CmdPartDesignLinearPattern() void CmdPartDesignLinearPattern::activated(int iMsg) { - // Get a valid original from the user - // First check selections - std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); - std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); - // Next create a list of all eligible objects - if (features.size() == 0) { - features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); - subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Get a valid original from the user + // First check selections + std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); + std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Next create a list of all eligible objects + if (features.size() == 0) { + features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); + subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); // If there is more than one selected or eligible object, show dialog and let user pick one if (features.size() > 1) { PartDesignGui::FeaturePickDialog Dlg(features); if ((Dlg.exec() != QDialog::Accepted) || (features = Dlg.getFeatures()).empty()) return; // Cancelled or nothing selected - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a subtractive or additive feature first, please")); - return; - } - } + } else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), + QObject::tr("Please create a subtractive or additive feature first, please")); + return; + } + } std::string FeatName = getUniqueObjectName("LinearPattern"); std::stringstream str; std::vector tempSelNames; str << "App.activeDocument()." << FeatName << ".Originals = ["; - for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ - str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; - tempSelNames.push_back((*it)->getNameInDocument()); + for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ + str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; + tempSelNames.push_back((*it)->getNameInDocument()); } str << "]"; @@ -892,36 +1001,36 @@ CmdPartDesignPolarPattern::CmdPartDesignPolarPattern() void CmdPartDesignPolarPattern::activated(int iMsg) { - // Get a valid original from the user - // First check selections - std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); - std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); - // Next create a list of all eligible objects - if (features.size() == 0) { - features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); - subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Get a valid original from the user + // First check selections + std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); + std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Next create a list of all eligible objects + if (features.size() == 0) { + features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); + subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); // If there is more than one selected or eligible object, show dialog and let user pick one if (features.size() > 1) { PartDesignGui::FeaturePickDialog Dlg(features); if ((Dlg.exec() != QDialog::Accepted) || (features = Dlg.getFeatures()).empty()) return; // Cancelled or nothing selected - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a subtractive or additive feature first, please")); - return; - } - } + } else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), + QObject::tr("Please create a subtractive or additive feature first, please")); + return; + } + } std::string FeatName = getUniqueObjectName("PolarPattern"); std::stringstream str; std::vector tempSelNames; str << "App.activeDocument()." << FeatName << ".Originals = ["; - for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ - str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; - tempSelNames.push_back((*it)->getNameInDocument()); + for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ + str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; + tempSelNames.push_back((*it)->getNameInDocument()); } str << "]"; @@ -966,36 +1075,36 @@ CmdPartDesignScaled::CmdPartDesignScaled() void CmdPartDesignScaled::activated(int iMsg) { - // Get a valid original from the user - // First check selections - std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); - std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); - // Next create a list of all eligible objects - if (features.size() == 0) { - features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); - subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Get a valid original from the user + // First check selections + std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); + std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Next create a list of all eligible objects + if (features.size() == 0) { + features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); + subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); // If there is more than one selected or eligible object, show dialog and let user pick one if (features.size() > 1) { PartDesignGui::FeaturePickDialog Dlg(features); if ((Dlg.exec() != QDialog::Accepted) || (features = Dlg.getFeatures()).empty()) return; // Cancelled or nothing selected - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a subtractive or additive feature first, please")); - return; - } - } + } else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), + QObject::tr("Please create a subtractive or additive feature first, please")); + return; + } + } std::string FeatName = getUniqueObjectName("Scaled"); std::stringstream str; std::vector tempSelNames; str << "App.activeDocument()." << FeatName << ".Originals = ["; - for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ - str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; - tempSelNames.push_back((*it)->getNameInDocument()); + for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ + str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; + tempSelNames.push_back((*it)->getNameInDocument()); } str << "]"; @@ -1039,36 +1148,36 @@ CmdPartDesignMultiTransform::CmdPartDesignMultiTransform() void CmdPartDesignMultiTransform::activated(int iMsg) { - // Get a valid original from the user - // First check selections - std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); - std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); - // Next create a list of all eligible objects - if (features.size() == 0) { - features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); - subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); - features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Get a valid original from the user + // First check selections + std::vector features = getSelection().getObjectsOfType(PartDesign::Additive::getClassTypeId()); + std::vector subtractive = getSelection().getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); + // Next create a list of all eligible objects + if (features.size() == 0) { + features = getDocument()->getObjectsOfType(PartDesign::Additive::getClassTypeId()); + subtractive = getDocument()->getObjectsOfType(PartDesign::Subtractive::getClassTypeId()); + features.insert(features.end(), subtractive.begin(), subtractive.end()); // If there is more than one selected or eligible object, show dialog and let user pick one if (features.size() > 1) { PartDesignGui::FeaturePickDialog Dlg(features); if ((Dlg.exec() != QDialog::Accepted) || (features = Dlg.getFeatures()).empty()) return; // Cancelled or nothing selected - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a subtractive or additive feature first, please")); - return; - } - } + } else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), + QObject::tr("Please create a subtractive or additive feature first, please")); + return; + } + } std::string FeatName = getUniqueObjectName("MultiTransform"); std::stringstream str; std::vector tempSelNames; str << "App.activeDocument()." << FeatName << ".Originals = ["; - for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ - str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; - tempSelNames.push_back((*it)->getNameInDocument()); + for (std::vector::iterator it = features.begin(); it != features.end(); ++it){ + str << "App.activeDocument()." << (*it)->getNameInDocument() << ","; + tempSelNames.push_back((*it)->getNameInDocument()); } str << "]"; @@ -1103,6 +1212,7 @@ void CreatePartDesignCommands(void) rcCmdMgr.addCommand(new CmdPartDesignRevolution()); rcCmdMgr.addCommand(new CmdPartDesignGroove()); rcCmdMgr.addCommand(new CmdPartDesignFillet()); + rcCmdMgr.addCommand(new CmdPartDesignDraft()); //rcCmdMgr.addCommand(new CmdPartDesignNewSketch()); rcCmdMgr.addCommand(new CmdPartDesignChamfer()); rcCmdMgr.addCommand(new CmdPartDesignMirrored()); diff --git a/src/Mod/PartDesign/Gui/Makefile.am b/src/Mod/PartDesign/Gui/Makefile.am index d9a3557ec..cddbc86a8 100644 --- a/src/Mod/PartDesign/Gui/Makefile.am +++ b/src/Mod/PartDesign/Gui/Makefile.am @@ -8,6 +8,7 @@ BUILT_SOURCES=\ moc_TaskPocketParameters.cpp \ moc_TaskChamferParameters.cpp \ moc_TaskFilletParameters.cpp \ + moc_TaskDraftParameters.cpp \ moc_TaskGrooveParameters.cpp \ moc_TaskHoleParameters.cpp \ moc_TaskLinearPatternParameters.cpp \ @@ -24,6 +25,7 @@ BUILT_SOURCES=\ ui_TaskPocketParameters.h \ ui_TaskChamferParameters.h \ ui_TaskFilletParameters.h \ + ui_TaskDraftParameters.h \ ui_TaskHoleParameters.h \ ui_TaskLinearPatternParameters.h \ ui_TaskMirroredParameters.h \ @@ -40,6 +42,7 @@ libPartDesignGui_la_UI=\ TaskPocketParameters.ui \ TaskChamferParameters.ui \ TaskFilletParameters.ui \ + TaskFilletParameters.ui \ TaskHoleParameters.ui \ TaskLinearPatternParameters.ui \ TaskMirroredParameters.ui \ @@ -68,6 +71,8 @@ libPartDesignGui_la_SOURCES=\ TaskChamferParameters.h \ TaskFilletParameters.cpp \ TaskFilletParameters.h \ + TaskDraftParameters.cpp \ + TaskDraftParameters.h \ TaskLinearPatternParameters.cpp \ TaskLinearPatternParameters.h \ TaskMirroredParameters.cpp \ @@ -106,6 +111,8 @@ libPartDesignGui_la_SOURCES=\ ViewProviderChamfer.h \ ViewProviderFillet.cpp \ ViewProviderFillet.h \ + ViewProviderDraft.cpp \ + ViewProviderDraft.h \ ViewProviderGroove.cpp \ ViewProviderGroove.h \ ViewProviderRevolution.cpp \ diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg new file mode 100644 index 000000000..e69de29bb diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp new file mode 100644 index 000000000..9b31abe20 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp @@ -0,0 +1,432 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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_ +#endif + +#include "ui_TaskDraftParameters.h" +#include "TaskDraftParameters.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace PartDesignGui; +using namespace Gui; + +/* TRANSLATOR PartDesignGui::TaskDraftParameters */ + +TaskDraftParameters::TaskDraftParameters(ViewProviderDraft *DraftView,QWidget *parent) + : TaskBox(Gui::BitmapFactory().pixmap("Part_Draft"),tr("Draft parameters"),true, parent),DraftView(DraftView) +{ + selectionMode = none; + + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskDraftParameters(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(onAngleChanged(double))); + connect(ui->checkReverse, SIGNAL(toggled(bool)), + this, SLOT(onReversedChanged(bool))); + connect(ui->buttonFaceAdd, SIGNAL(toggled(bool)), + this, SLOT(onButtonFaceAdd(bool))); + connect(ui->buttonFaceRemove, SIGNAL(toggled(bool)), + this, SLOT(onButtonFaceRemove(bool))); + connect(ui->buttonPlane, SIGNAL(toggled(bool)), + this, SLOT(onButtonPlane(bool))); + connect(ui->buttonLine, SIGNAL(toggled(bool)), + this, SLOT(onButtonLine(bool))); + + this->groupLayout()->addWidget(proxy); + + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + double a = pcDraft->Angle.getValue(); + + ui->doubleSpinBox->setMinimum(0.0); + ui->doubleSpinBox->setMaximum(89.99); + ui->doubleSpinBox->setValue(a); + ui->doubleSpinBox->selectAll(); + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + + bool r = pcDraft->Reversed.getValue(); + ui->checkReverse->setChecked(r); + + std::vector strings = pcDraft->Base.getSubValues(); + for (std::vector::const_iterator i = strings.begin(); i != strings.end(); i++) + { + ui->listWidgetFaces->insertItem(0, QString::fromStdString(*i)); + } + // Create context menu + QAction* action = new QAction(tr("Remove"), this); + ui->listWidgetFaces->addAction(action); + connect(action, SIGNAL(triggered()), this, SLOT(onFaceDeleted())); + ui->listWidgetFaces->setContextMenuPolicy(Qt::ActionsContextMenu); + + strings = pcDraft->NeutralPlane.getSubValues(); + std::string neutralPlane = (strings.empty() ? "" : strings[0]); + ui->linePlane->setText(QString::fromStdString(neutralPlane)); + + strings = pcDraft->PullDirection.getSubValues(); + std::string pullDirection = (strings.empty() ? "" : strings[0]); + ui->lineLine->setText(QString::fromStdString(pullDirection)); +} + +void TaskDraftParameters::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (selectionMode == none) + return; + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + + if (strcmp(msg.pDocName, DraftView->getObject()->getDocument()->getName()) != 0) + return; + + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + App::DocumentObject* base = this->getBase(); + // TODO: Must we make a copy here instead of assigning to const char* ? + const char* fname = base->getNameInDocument(); + std::string subName(msg.pSubName); + + if ((selectionMode == faceAdd) && (subName.size() > 4 && subName.substr(0,4) == "Face")) { + + if (strcmp(msg.pObjectName, fname) != 0) + return; + + std::vector faces = pcDraft->Base.getSubValues(); + if (std::find(faces.begin(), faces.end(), subName) == faces.end()) { + faces.push_back(subName); + pcDraft->Base.setValue(base, faces); + ui->listWidgetFaces->insertItem(0, QString::fromStdString(subName)); + + pcDraft->getDocument()->recomputeFeature(pcDraft); + ui->buttonFaceAdd->setChecked(false); + exitSelectionMode(); + } + } else if ((selectionMode == faceRemove) && (subName.size() > 4 && subName.substr(0,4) == "Face")) { + + if (strcmp(msg.pObjectName, fname) != 0) + return; + + std::vector faces = pcDraft->Base.getSubValues(); + std::vector::iterator f = std::find(faces.begin(), faces.end(), subName); + if (f != faces.end()) { + faces.erase(f); + pcDraft->Base.setValue(base, faces); + QList items = ui->listWidgetFaces->findItems(QString::fromStdString(subName), Qt::MatchExactly); + if (!items.empty()) { + for (QList::const_iterator i = items.begin(); i != items.end(); i++) { + QListWidgetItem* it = ui->listWidgetFaces->takeItem(ui->listWidgetFaces->row(*i)); + delete it; + } + } + pcDraft->getDocument()->recomputeFeature(pcDraft); + ui->buttonFaceRemove->setChecked(false); + exitSelectionMode(); + } + } else if ((selectionMode == plane) && (subName.size() > 4) && + ((subName.substr(0,4) == "Face") || (subName.substr(0,4) == "Edge"))) { + + if (strcmp(msg.pObjectName, fname) != 0) + return; + + std::vector planes(1,subName); + pcDraft->NeutralPlane.setValue(base, planes); + ui->linePlane->setText(QString::fromStdString(subName)); + + pcDraft->getDocument()->recomputeFeature(pcDraft); + ui->buttonPlane->setChecked(false); + exitSelectionMode(); + } else if ((selectionMode == line) && (subName.size() > 4 && subName.substr(0,4) == "Edge")) { + + if (strcmp(msg.pObjectName, fname) != 0) + return; + + std::vector edges(1,subName); + pcDraft->PullDirection.setValue(base, edges); + ui->lineLine->setText(QString::fromStdString(subName)); + + pcDraft->getDocument()->recomputeFeature(pcDraft); + ui->buttonLine->setChecked(false); + exitSelectionMode(); + } + } +} + +void TaskDraftParameters::onButtonFaceAdd(bool checked) +{ + if (checked) { + hideObject(); + selectionMode = faceAdd; + Gui::Selection().clearSelection(); + Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), false, true, false)); + } else { + exitSelectionMode(); + } +} + +void TaskDraftParameters::onButtonFaceRemove(bool checked) +{ + if (checked) { + hideObject(); + selectionMode = faceRemove; + Gui::Selection().clearSelection(); + Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), false, true, false)); + } else { + exitSelectionMode(); + } +} + +void TaskDraftParameters::onButtonPlane(bool checked) +{ + if (checked) { + hideObject(); + selectionMode = plane; + Gui::Selection().clearSelection(); + Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), true, true, true)); + } else { + exitSelectionMode(); + } +} + +void TaskDraftParameters::onButtonLine(bool checked) +{ + if (checked) { + hideObject(); + selectionMode = line; + Gui::Selection().clearSelection(); + Gui::Selection().addSelectionGate(new ReferenceSelection(this->getBase(), true, false, true)); + } else { + exitSelectionMode(); + } +} + +const std::vector TaskDraftParameters::getFaces(void) const +{ + std::vector result; + for (int i = 0; i < ui->listWidgetFaces->count(); i++) + result.push_back(ui->listWidgetFaces->item(i)->text().toStdString()); + return result; +} + +void TaskDraftParameters::onFaceDeleted(void) +{ + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + App::DocumentObject* base = pcDraft->Base.getValue(); + std::vector faces = pcDraft->Base.getSubValues(); + faces.erase(faces.begin() + ui->listWidgetFaces->currentRow()); + pcDraft->Base.setValue(base, faces); + ui->listWidgetFaces->model()->removeRow(ui->listWidgetFaces->currentRow()); + pcDraft->getDocument()->recomputeFeature(pcDraft); +} + +const std::string TaskDraftParameters::getPlane(void) const +{ + return ui->linePlane->text().toStdString(); +} + +const std::string TaskDraftParameters::getLine(void) const +{ + return ui->lineLine->text().toStdString(); +} + +void TaskDraftParameters::hideObject() +{ + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + App::DocumentObject* base = getBase(); + if (doc != NULL && base != NULL) { + doc->setHide(DraftView->getObject()->getNameInDocument()); + doc->setShow(base->getNameInDocument()); + } +} + +void TaskDraftParameters::showObject() +{ + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + App::DocumentObject* base = getBase(); + if (doc != NULL && base != NULL) { + doc->setShow(DraftView->getObject()->getNameInDocument()); + doc->setHide(base->getNameInDocument()); + } +} + +void TaskDraftParameters::onAngleChanged(double angle) +{ + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + pcDraft->Angle.setValue((float)angle); + pcDraft->getDocument()->recomputeFeature(pcDraft); +} + +const double TaskDraftParameters::getAngle(void) const +{ + return ui->doubleSpinBox->value(); +} + +void TaskDraftParameters::onReversedChanged(const bool on) { + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + pcDraft->Reversed.setValue(on); + pcDraft->getDocument()->recomputeFeature(pcDraft); +} + +const bool TaskDraftParameters::getReversed(void) const +{ + return ui->checkReverse->isChecked(); +} + +App::DocumentObject* TaskDraftParameters::getBase(void) const +{ + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + return pcDraft->Base.getValue(); +} + +TaskDraftParameters::~TaskDraftParameters() +{ + delete ui; +} + +void TaskDraftParameters::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(proxy); + } +} + +void TaskDraftParameters::exitSelectionMode() +{ + selectionMode = none; + Gui::Selection().rmvSelectionGate(); + showObject(); +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgDraftParameters::TaskDlgDraftParameters(ViewProviderDraft *DraftView) + : TaskDialog(),DraftView(DraftView) +{ + assert(DraftView); + parameter = new TaskDraftParameters(DraftView); + + Content.push_back(parameter); +} + +TaskDlgDraftParameters::~TaskDlgDraftParameters() +{ + +} + +//==== calls from the TaskView =============================================================== + + +void TaskDlgDraftParameters::open() +{ + +} + +void TaskDlgDraftParameters::clicked(int) +{ + +} + +bool TaskDlgDraftParameters::accept() +{ + std::string name = DraftView->getObject()->getNameInDocument(); + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Angle = %f",name.c_str(),parameter->getAngle()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %u",name.c_str(),parameter->getReversed()); + try { + std::vector faces = parameter->getFaces(); + std::stringstream str; + str << "App.ActiveDocument." << name.c_str() << ".Base = (App.ActiveDocument." + << parameter->getBase()->getNameInDocument() << ",["; + for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) + str << "\"" << *it << "\","; + str << "])"; + Gui::Command::doCommand(Gui::Command::Doc,str.str().c_str()); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + std::string neutralPlane = parameter->getPlane(); + if (!neutralPlane.empty()) { + QString buf = QString::fromUtf8("(App.ActiveDocument.%1,[\"%2\"])"); + buf = buf.arg(QString::fromUtf8(parameter->getBase()->getNameInDocument())); + buf = buf.arg(QString::fromUtf8(neutralPlane.c_str())); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.NeutralPlane = %s", name.c_str(), buf.toStdString().c_str()); + } else + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.NeutralPlane = None", name.c_str()); + std::string pullDirection = parameter->getLine(); + if (!pullDirection.empty()) { + QString buf = QString::fromUtf8("(App.ActiveDocument.%1,[\"%2\"])"); + buf = buf.arg(QString::fromUtf8(parameter->getBase()->getNameInDocument())); + buf = buf.arg(QString::fromUtf8(pullDirection.c_str())); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.PullDirection = %s", name.c_str(), buf.toStdString().c_str()); + } else + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.PullDirection = None", name.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + Gui::Command::commitCommand(); + + return true; +} + +bool TaskDlgDraftParameters::reject() +{ + // get the support + PartDesign::Draft* pcDraft = static_cast(DraftView->getObject()); + App::DocumentObject *pcSupport; + pcSupport = pcDraft->Base.getValue(); + + // roll back the done things + Gui::Command::abortCommand(); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + + // if abort command deleted the object the support is visible again + if (!Gui::Application::Instance->getViewProvider(pcDraft)) { + if (pcSupport && Gui::Application::Instance->getViewProvider(pcSupport)) + Gui::Application::Instance->getViewProvider(pcSupport)->show(); + } + + return true; +} + + + +#include "moc_TaskDraftParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.h b/src/Mod/PartDesign/Gui/TaskDraftParameters.h new file mode 100644 index 000000000..1508ee3c2 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.h @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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 GUI_TASKVIEW_TaskDraftParameters_H +#define GUI_TASKVIEW_TaskDraftParameters_H + +#include +#include +#include + +#include "ViewProviderDraft.h" + +class Ui_TaskDraftParameters; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + + +namespace PartDesignGui { + +class TaskDraftParameters : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +{ + Q_OBJECT + +public: + TaskDraftParameters(ViewProviderDraft *DraftView, QWidget *parent=0); + ~TaskDraftParameters(); + + const double getAngle(void) const; + const bool getReversed(void) const; + const std::vector getFaces(void) const; + const std::string getPlane(void) const; + const std::string getLine(void) const; + App::DocumentObject *getBase(void) const; + +private Q_SLOTS: + void onAngleChanged(double angle); + void onReversedChanged(bool reversed); + void onButtonFaceAdd(const bool checked); + void onButtonFaceRemove(const bool checked); + void onButtonPlane(const bool checked); + void onButtonLine(const bool checked); + void onFaceDeleted(void); + +protected: + void hideObject(); + void showObject(); + void exitSelectionMode(); + +protected: + void changeEvent(QEvent *e); + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +private: + QWidget* proxy; + Ui_TaskDraftParameters* ui; + ViewProviderDraft *DraftView; + + enum selectionModes { none, faceAdd, faceRemove, plane, line }; + selectionModes selectionMode; +}; + +/// simulation dialog for the TaskView +class TaskDlgDraftParameters : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgDraftParameters(ViewProviderDraft *DraftView); + ~TaskDlgDraftParameters(); + + ViewProviderDraft* getDraftView() const + { return DraftView; } + + +public: + /// is called the TaskView when the dialog is opened + virtual void open(); + /// is called by the framework if an button is clicked which has no accept or reject role + virtual void clicked(int); + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + /// is called by the framework if the dialog is rejected (Cancel) + virtual bool reject(); + /// is called by the framework if the user presses the help button + virtual bool isAllowedAlterDocument(void) const + { return false; } + + /// returns for Close and Help button + virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + +protected: + ViewProviderDraft *DraftView; + + TaskDraftParameters *parameter; +}; + +} //namespace PartDesignGui + +#endif // GUI_TASKVIEW_TASKAPPERANCE_H diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.ui b/src/Mod/PartDesign/Gui/TaskDraftParameters.ui new file mode 100644 index 000000000..fb135fa98 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.ui @@ -0,0 +1,120 @@ + + + PartDesignGui::TaskDraftParameters + + + + 0 + 0 + 257 + 285 + + + + Form + + + + + + + + Add face + + + true + + + + + + + Remove face + + + true + + + + + + + + + + + + + + Draft angle + + + + + + + 0.000000000000000 + + + 89.000000000000000 + + + 0.100000000000000 + + + 1.500000000000000 + + + + + + + + + + + Neutral plane + + + true + + + + + + + + + + + + + + Pull direction + + + true + + + + + + + + + + + + Reverse pull direction + + + + + checkReverse + listWidgetFaces + buttonFaceAdd + buttonFaceRemove + + + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderDraft.cpp b/src/Mod/PartDesign/Gui/ViewProviderDraft.cpp new file mode 100644 index 000000000..da7079aa5 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDraft.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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_ +#endif + +#include "ViewProviderDraft.h" +#include "TaskDraftParameters.h" +#include +#include +#include +#include +#include + + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderDraft,PartDesignGui::ViewProvider) + +ViewProviderDraft::ViewProviderDraft() +{ +} + +ViewProviderDraft::~ViewProviderDraft() +{ +} + + +void ViewProviderDraft::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + QAction* act; + act = menu->addAction(QObject::tr("Edit draft"), receiver, member); + act->setData(QVariant((int)ViewProvider::Default)); + PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member); +} + +bool ViewProviderDraft::setEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this fillet the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgDraftParameters *draftDlg = qobject_cast(dlg); + if (draftDlg && draftDlg->getDraftView() != this) + draftDlg = 0; // another pad left open its task panel + if (dlg && !draftDlg) { + QMessageBox msgBox; + msgBox.setText(QObject::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) + Gui::Control().closeDialog(); + else + return false; + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + //if(ModNum == 1) + // Gui::Command::openCommand("Change draft parameters"); + + // start the edit dialog + if (draftDlg) + Gui::Control().showDialog(draftDlg); + else + Gui::Control().showDialog(new TaskDlgDraftParameters(this)); + + return true; + } + else { + return PartGui::ViewProviderPart::setEdit(ModNum); + } +} + +void ViewProviderDraft::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default ) { + // and update the draft + //getSketchObject()->getDocument()->recompute(); + + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); + } + else { + PartGui::ViewProviderPart::unsetEdit(ModNum); + } +} + +bool ViewProviderDraft::onDelete(const std::vector &) +{ + // get the support and Sketch + PartDesign::Draft* pcDraft = static_cast(getObject()); + App::DocumentObject *pcSupport = 0; + if (pcDraft->Base.getValue()){ + pcSupport = static_cast(pcDraft->Base.getValue()); + } + + // if abort command deleted the object the support is visible again + if (pcSupport && Gui::Application::Instance->getViewProvider(pcSupport)) + Gui::Application::Instance->getViewProvider(pcSupport)->show(); + + return true; +} + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderDraft.h b/src/Mod/PartDesign/Gui/ViewProviderDraft.h new file mode 100644 index 000000000..07829d2ea --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDraft.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2012 Jan Rheinländer * + * * + * 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_ViewProviderDraft_H +#define PARTGUI_ViewProviderDraft_H + +#include "ViewProvider.h" + + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderDraft : public ViewProvider +{ + PROPERTY_HEADER(PartDesignGui::ViewProviderDraft); + +public: + /// constructor + ViewProviderDraft(); + /// destructor + virtual ~ViewProviderDraft(); + + /// grouping handling + void setupContextMenu(QMenu*, QObject*, const char*); + + virtual bool onDelete(const std::vector &); + +protected: + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); + +}; + + + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderDraft_H diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 7a3b472a2..5a11069c1 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -86,6 +86,7 @@ void Workbench::activated() "Sketcher_NewSketch", "PartDesign_Fillet", "PartDesign_Chamfer", + "PartDesign_Draft", 0}; Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( "SELECT Part::Feature SUBELEMENT Face COUNT 1", @@ -97,6 +98,7 @@ void Workbench::activated() const char* Faces[] = { "PartDesign_Fillet", "PartDesign_Chamfer", + "PartDesign_Draft", 0}; Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( "SELECT Part::Feature SUBELEMENT Face COUNT 2..", @@ -212,6 +214,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "PartDesign_Groove" << "PartDesign_Fillet" << "PartDesign_Chamfer" + << "PartDesign_Draft" << "PartDesign_Mirrored" << "PartDesign_LinearPattern" << "PartDesign_PolarPattern" @@ -243,6 +246,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "PartDesign_Groove" << "PartDesign_Fillet" << "PartDesign_Chamfer" + << "PartDesign_Draft" << "PartDesign_Mirrored" << "PartDesign_LinearPattern" << "PartDesign_PolarPattern" From 3871b75d3ea82a61a537e3d828d98c014a4f3c21 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Tue, 27 Nov 2012 19:45:06 +0430 Subject: [PATCH 05/15] Some notes on pad and draft features for future developments --- src/Mod/PartDesign/App/FeaturePad.cpp | 2 ++ src/Mod/PartDesign/Gui/Command.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/Mod/PartDesign/App/FeaturePad.cpp b/src/Mod/PartDesign/App/FeaturePad.cpp index 47e459204..f2f99784e 100644 --- a/src/Mod/PartDesign/App/FeaturePad.cpp +++ b/src/Mod/PartDesign/App/FeaturePad.cpp @@ -141,6 +141,8 @@ App::DocumentObjectExecReturn *Pad::execute(void) // because the feature does not add any material. This only happens with the "2" option, though // Note: It might be possible to pass a shell or a compound containing multiple faces // as the Until parameter of Perform() + // Note: Multiple independent wires are not supported, we should check for that and + // warn the user BRepFeat_MakePrism PrismMaker; PrismMaker.Init(support, sketchshape, supportface, dir, 2, 1); PrismMaker.Perform(upToFace); diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 700a6dda7..4bb0b006a 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -814,6 +814,9 @@ void CmdPartDesignDraft::activated(int iMsg) // We don't create any defaults for neutral plane and pull direction, but Draft::execute() // will choose them. + // Note: When the body feature is there, the best thing would be to get pull direction and + // neutral plane from the preceding feature in the tree. Or even store them as default in + // the Body feature itself openCommand("Make Draft"); doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Draft\",\"%s\")",FeatName.c_str()); doCommand(Doc,"App.activeDocument().%s.Base = %s",FeatName.c_str(),SelString.c_str()); From ac6f7a434b85f1f978c7aeddef69c7115951fc1a Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Wed, 28 Nov 2012 09:03:00 +0430 Subject: [PATCH 06/15] Removed unnecessary boundary check for FloatConstraint property --- src/Mod/PartDesign/App/FeatureDraft.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index 16dfb3f59..bc191ea65 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -101,10 +101,7 @@ App::DocumentObjectExecReturn *Draft::execute(void) return new App::DocumentObjectExecReturn("No faces specified"); // Draft angle - float angle = Angle.getValue(); - if ((angle < 0.0) || (angle >= 90.0)) - return new App::DocumentObjectExecReturn("Draft angle must be between 0 and 90 degrees"); - angle = angle / 180.0 * M_PI; + float angle = Angle.getValue() / 180.0 * M_PI; // Pull direction gp_Dir pullDirection; From d7630539251dc3ba6939930c2bc9857b50356b62 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Fri, 30 Nov 2012 13:34:02 +0430 Subject: [PATCH 07/15] Fixed two bugs, thanks to wmayer --- src/Mod/PartDesign/App/FeatureDraft.cpp | 5 ++--- src/Mod/PartDesign/Gui/TaskDraftParameters.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index bc191ea65..f3ba1eb21 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -161,12 +161,11 @@ App::DocumentObjectExecReturn *Draft::execute(void) // Edge is linear // Find midpoint of edge and create auxiliary plane through midpoint normal to edge gp_Pnt pm = c.Value((c.FirstParameter() + c.LastParameter()) / 2.0); - Geom_Plane aux(pm, gp_Dir(p2.X() - p1.X(), p2.Y() - p1.Y(), p2.Z() - p1.Z())); + Handle_Geom_Plane aux = new Geom_Plane(pm, gp_Dir(p2.X() - p1.X(), p2.Y() - p1.Y(), p2.Z() - p1.Z())); // Intersect plane with face. Is there no easier way? BRepAdaptor_Surface adapt(TopoDS::Face(face), Standard_False); Handle_Geom_Surface sf = adapt.Surface().Surface(); - //Handle_Geom_Surface auxsf(&aux); - GeomAPI_IntSS intersector(&aux, sf, Precision::Confusion()); + GeomAPI_IntSS intersector(aux, sf, Precision::Confusion()); if (!intersector.IsDone()) continue; Handle_Geom_Curve icurve = intersector.Line(1); diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp index 9b31abe20..68b8143d5 100644 --- a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp @@ -48,7 +48,7 @@ using namespace Gui; /* TRANSLATOR PartDesignGui::TaskDraftParameters */ TaskDraftParameters::TaskDraftParameters(ViewProviderDraft *DraftView,QWidget *parent) - : TaskBox(Gui::BitmapFactory().pixmap("Part_Draft"),tr("Draft parameters"),true, parent),DraftView(DraftView) + : TaskBox(Gui::BitmapFactory().pixmap("PartDesign_Draft"),tr("Draft parameters"),true, parent),DraftView(DraftView) { selectionMode = none; From 37b3c3df0f69ee647f0007e44ca00a323ab3e607 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Fri, 30 Nov 2012 14:32:19 +0430 Subject: [PATCH 08/15] Icon for PartDesign_Draft --- src/Mod/PartDesign/Gui/Resources/Makefile.am | 1 + .../PartDesign/Gui/Resources/PartDesign.qrc | 1 + .../Gui/Resources/icons/PartDesign_Draft.svg | 204 ++++++++++++++++++ 3 files changed, 206 insertions(+) diff --git a/src/Mod/PartDesign/Gui/Resources/Makefile.am b/src/Mod/PartDesign/Gui/Resources/Makefile.am index c8376b909..3f83e8ba6 100644 --- a/src/Mod/PartDesign/Gui/Resources/Makefile.am +++ b/src/Mod/PartDesign/Gui/Resources/Makefile.am @@ -53,6 +53,7 @@ EXTRA_DIST = \ translations/PartDesign_tr.ts \ icons/PartDesign_Chamfer.svg \ icons/PartDesign_Fillet.svg \ + icons/PartDesign_Draft.svg \ icons/PartDesign_Groove.svg \ icons/PartDesign_Pad.svg \ icons/PartDesign_Pocket.svg \ diff --git a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc index 5340634d8..9b41c75a6 100644 --- a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc +++ b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc @@ -2,6 +2,7 @@ icons/PartDesign_Chamfer.svg icons/PartDesign_Fillet.svg + icons/PartDesign_Draft.svg icons/PartDesign_Groove.svg icons/PartDesign_Pad.svg icons/PartDesign_Pocket.svg diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg index e69de29bb..6881049b5 100644 --- a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg +++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + From 8a1c9f0651d7a3f7e36467b7f3b15e1c5a8e30ed Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Fri, 30 Nov 2012 15:58:38 +0430 Subject: [PATCH 09/15] Fixed UI bugs --- src/Mod/PartDesign/Gui/TaskDraftParameters.cpp | 10 ++++++++++ src/Mod/PartDesign/Gui/TaskDraftParameters.h | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp index 68b8143d5..e2a0e850c 100644 --- a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +367,15 @@ void TaskDlgDraftParameters::clicked(int) bool TaskDlgDraftParameters::accept() { + parameter->showObject(); + + // Force the user to select a neutral plane + if (parameter->getPlane().empty()) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Missing neutral plane"), + QObject::tr("Please select a plane or an edge plus a pull direction")); + return false; + } + std::string name = DraftView->getObject()->getNameInDocument(); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Angle = %f",name.c_str(),parameter->getAngle()); diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.h b/src/Mod/PartDesign/Gui/TaskDraftParameters.h index 1508ee3c2..9f95bb9c3 100644 --- a/src/Mod/PartDesign/Gui/TaskDraftParameters.h +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.h @@ -58,6 +58,9 @@ public: const std::string getLine(void) const; App::DocumentObject *getBase(void) const; + void hideObject(); + void showObject(); + private Q_SLOTS: void onAngleChanged(double angle); void onReversedChanged(bool reversed); @@ -68,8 +71,6 @@ private Q_SLOTS: void onFaceDeleted(void); protected: - void hideObject(); - void showObject(); void exitSelectionMode(); protected: From 8371982dfc3863e6b142d57b54f01f9bea1af8cc Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Fri, 30 Nov 2012 17:29:07 +0430 Subject: [PATCH 10/15] Added check to Revolution and Groove for sketch axis intersecting the sketch face --- src/Mod/PartDesign/App/FeatureGroove.cpp | 13 ++-- src/Mod/PartDesign/App/FeatureRevolution.cpp | 14 +++-- src/Mod/PartDesign/App/FeatureSketchBased.cpp | 60 +++++++++++++++++++ src/Mod/PartDesign/App/FeatureSketchBased.h | 7 ++- 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureGroove.cpp b/src/Mod/PartDesign/App/FeatureGroove.cpp index 763016932..006abdeea 100644 --- a/src/Mod/PartDesign/App/FeatureGroove.cpp +++ b/src/Mod/PartDesign/App/FeatureGroove.cpp @@ -33,6 +33,7 @@ # include # include # include +# include #endif #include @@ -76,12 +77,12 @@ App::DocumentObjectExecReturn *Groove::execute(void) return new App::DocumentObjectExecReturn("Angle of groove too small"); if (angle > 360.0) return new App::DocumentObjectExecReturn("Angle of groove too large"); - + angle = Base::toRadians(angle); // Reverse angle if selected if (Reversed.getValue() && !Midplane.getValue()) angle *= (-1.0); - + Part::Part2DObject* sketch = 0; std::vector wires; TopoDS_Shape support; @@ -152,6 +153,10 @@ App::DocumentObjectExecReturn *Groove::execute(void) support.Move(invObjLoc); sketchshape.Move(invObjLoc); + // Check distance between sketchshape and axis - to avoid failures and crashes + if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(sketchshape))) + return new App::DocumentObjectExecReturn("Revolve axis intersects the sketch"); + // revolve the face to a solid BRepPrimAPI_MakeRevol RevolMaker(sketchshape, gp_Ax1(pnt, dir), angle); @@ -159,7 +164,7 @@ App::DocumentObjectExecReturn *Groove::execute(void) TopoDS_Shape result = RevolMaker.Shape(); // set the subtractive shape property for later usage in e.g. pattern this->SubShape.setValue(result); - + // cut out groove to get one result object BRepAlgoAPI_Cut mkCut(support, result); // Let's check if the fusion has been successful @@ -181,7 +186,7 @@ App::DocumentObjectExecReturn *Groove::execute(void) catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); if (std::string(e->GetMessageString()) == "TopoDS::Face") - return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" + return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed."); else return new App::DocumentObjectExecReturn(e->GetMessageString()); diff --git a/src/Mod/PartDesign/App/FeatureRevolution.cpp b/src/Mod/PartDesign/App/FeatureRevolution.cpp index 463c32970..c5018588f 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.cpp +++ b/src/Mod/PartDesign/App/FeatureRevolution.cpp @@ -33,6 +33,7 @@ # include # include # include +# include #endif #include @@ -40,6 +41,7 @@ #include #include "FeatureRevolution.h" +#include using namespace PartDesign; @@ -76,12 +78,12 @@ App::DocumentObjectExecReturn *Revolution::execute(void) return new App::DocumentObjectExecReturn("Angle of groove too small"); if (angle > 360.0) return new App::DocumentObjectExecReturn("Angle of groove too large"); - + angle = Base::toRadians(angle); // Reverse angle if selected if (Reversed.getValue() && !Midplane.getValue()) angle *= (-1.0); - + Part::Part2DObject* sketch = 0; std::vector wires; try { @@ -90,7 +92,7 @@ App::DocumentObjectExecReturn *Revolution::execute(void) } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } - + TopoDS_Shape support; try { support = getSupportShape(); @@ -158,6 +160,10 @@ App::DocumentObjectExecReturn *Revolution::execute(void) support.Move(invObjLoc); sketchshape.Move(invObjLoc); + // Check distance between sketchshape and axis - to avoid failures and crashes + if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(sketchshape))) + return new App::DocumentObjectExecReturn("Revolve axis intersects the sketch"); + // revolve the face to a solid BRepPrimAPI_MakeRevol RevolMaker(sketchshape, gp_Ax1(pnt, dir), angle); @@ -186,7 +192,7 @@ App::DocumentObjectExecReturn *Revolution::execute(void) catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); if (std::string(e->GetMessageString()) == "TopoDS::Face") - return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" + return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed."); else return new App::DocumentObjectExecReturn(e->GetMessageString()); diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.cpp b/src/Mod/PartDesign/App/FeatureSketchBased.cpp index 3f5bac665..5a9aaa41c 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.cpp +++ b/src/Mod/PartDesign/App/FeatureSketchBased.cpp @@ -52,6 +52,11 @@ # include # include # include +# include +# include +# include +# include +# include #endif @@ -532,6 +537,61 @@ const bool SketchBased::checkWireInsideFace(const TopoDS_Wire& wire, const TopoD return (proj.More() && proj.Current().Closed()); } +const bool SketchBased::checkLineCrossesFace(const gp_Lin &line, const TopoDS_Face &face) { + // This is not as easy as it looks, because a distance of zero might be OK if + // the axis touches the sketchshape in in a linear edge or a vertex + // Note: This algorithm does not catch cases where the sketchshape touches the + // axis in two or more points + // Note: And it only works on closed outer wires + TopoDS_Wire outerWire = ShapeAnalysis::OuterWire(face); + BRepBuilderAPI_MakeEdge mkEdge(line); + if (!mkEdge.IsDone()) + throw Base::Exception("Revolve: Unexpected OCE failure"); + BRepAdaptor_Curve axis(TopoDS::Edge(mkEdge.Shape())); + + TopExp_Explorer ex; + int intersections = 0; + std::vector intersectionpoints; + + // Note: We need to look at evey edge separately to catch coincident lines + for (ex.Init(outerWire, TopAbs_EDGE); ex.More(); ex.Next()) { + BRepAdaptor_Curve edge(TopoDS::Edge(ex.Current())); + Extrema_ExtCC intersector(axis, edge); + + if (intersector.IsDone()) { + for (int i = 1; i <= intersector.NbExt(); i++) { + + + if (intersector.SquareDistance(i) < Precision::Confusion()) { + if (intersector.IsParallel()) { + // A line that is coincident with the axis produces three intersections + // 1 with the line itself and 2 with the adjacent edges + intersections -= 2; + } else { + Extrema_POnCurv p1, p2; + intersector.Points(i, p1, p2); + intersectionpoints.push_back(p1.Value()); + intersections++; + } + } + } + } + } + + // Note: We might check this inside the loop but then we have to rely on TopExp_Explorer + // returning the wire's edges in adjacent order (because of the coincident line checking) + if (intersections > 1) { + // Check that we don't touch the sketchface just in two identical vertices + if ((intersectionpoints.size() == 2) && + (intersectionpoints[0].IsEqual(intersectionpoints[1], Precision::Confusion()))) + return false; + else + return true; + } + + return false; +} + void SketchBased::remapSupportShape(const TopoDS_Shape& newShape) { TopTools_IndexedMapOfShape faceMap; diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.h b/src/Mod/PartDesign/App/FeatureSketchBased.h index 4163ffe86..995a2e112 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.h +++ b/src/Mod/PartDesign/App/FeatureSketchBased.h @@ -32,6 +32,7 @@ class TopoDS_Shape; class TopoDS_Face; class TopoDS_Wire; class gp_Dir; +class gp_Lin; namespace PartDesign { @@ -102,7 +103,7 @@ protected: const std::string& method, const gp_Dir& direction, const double L, - const double L2, + const double L2, const bool midplane, const bool reversed); @@ -110,6 +111,10 @@ protected: static const bool checkWireInsideFace(const TopoDS_Wire& wire, const TopoDS_Face& face, const gp_Dir& dir); + + /// Check whether the line crosses the face (line and face must be on the same plane) + static const bool checkLineCrossesFace(const gp_Lin& line, const TopoDS_Face& face); + }; } //namespace PartDesign From 3d4bca27b97eac47c3592e3ae864c21de6386581 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 08:19:35 +0430 Subject: [PATCH 11/15] Added bore command to partdesign workbench --- src/Mod/PartDesign/Gui/Workbench.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 5a11069c1..5d39779d1 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -87,6 +87,7 @@ void Workbench::activated() "PartDesign_Fillet", "PartDesign_Chamfer", "PartDesign_Draft", + "PartDesign_Bore", 0}; Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( "SELECT Part::Feature SUBELEMENT Face COUNT 1", @@ -219,7 +220,9 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "PartDesign_LinearPattern" << "PartDesign_PolarPattern" // << "PartDesign_Scaled" - << "PartDesign_MultiTransform"; + << "PartDesign_MultiTransform" + << "Separator" + << "PartDesign_Bore" // For 0.13 a couple of python packages like numpy, matplotlib and others // are not deployed with the installer on Windows. Thus, the WizardShaft is // not deployed either hence the check for the existence of the command. From d6bd7f1a7d396151b07d28c70e28997f277460f7 Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 14:01:42 +0430 Subject: [PATCH 12/15] Added for bore feature --- src/Mod/PartDesign/CMakeLists.txt | 27 +++++++++++++++++++++++++-- src/Mod/PartDesign/InitGui.py | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/CMakeLists.txt b/src/Mod/PartDesign/CMakeLists.txt index 5a5821da0..72c01c533 100644 --- a/src/Mod/PartDesign/CMakeLists.txt +++ b/src/Mod/PartDesign/CMakeLists.txt @@ -41,12 +41,31 @@ SET(WizardShaft_SRCS ) SOURCE_GROUP("wizardshaft" FILES ${WizardShaft_SRCS}) -SET(all_files ${WizardShaft_SRCS}) +SET(all_wizardshaft_files ${WizardShaft_SRCS}) ADD_CUSTOM_TARGET(WizardShaft ALL - SOURCES ${all_files} + SOURCES ${all_wizardshaft_files} ) +SET(FeatureBore_SRCS + FeatureBore/__init__.py + FeatureBore/FeatureBore.py + FeatureBore/TaskBore.py +) +SOURCE_GROUP("featurebore" FILES ${FeatureBore_SRCS}) + +SET(FeatureBore_UI + FeatureBore/TaskBore.ui +) + +SET(all_featurebore_files ${FeatureBore_SRCS} ${FeatureBore_UI}) + +ADD_CUSTOM_TARGET(FeatureBore ALL + SOURCES ${all_featurebore_files} +) + +SET(all_files ${all_featurebore_files} ${all_wizardshaft_files}) + fc_copy_sources(Mod/PartDesign "${CMAKE_BINARY_DIR}/Mod/PartDesign" ${all_files}) INSTALL( @@ -54,4 +73,8 @@ INSTALL( ${WizardShaft_SRCS} DESTINATION Mod/PartDesign/WizardShaft + FILES + ${FeatureBore_SRCS} + DESTINATION + Mod/PartDesign/FeatureBore ) diff --git a/src/Mod/PartDesign/InitGui.py b/src/Mod/PartDesign/InitGui.py index 8b590eafb..6f67a02b7 100644 --- a/src/Mod/PartDesign/InitGui.py +++ b/src/Mod/PartDesign/InitGui.py @@ -31,6 +31,7 @@ class PartDesignWorkbench ( Workbench ): "PartDesign workbench object" + from FeatureBore import TaskBore Icon = """ /* XPM */ static char * partdesign_xpm[] = { From 40e3fa7a21978dec11762b05e3d463ef64e9fd8e Mon Sep 17 00:00:00 2001 From: jrheinlaender Date: Mon, 3 Dec 2012 19:43:29 +0430 Subject: [PATCH 13/15] FeatureDraft: Omitted use of Remove() because of OCC bug --- src/Mod/PartDesign/App/FeatureDraft.cpp | 35 ++++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index f3ba1eb21..38ed3e713 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -96,7 +96,8 @@ App::DocumentObjectExecReturn *Draft::execute(void) return new App::DocumentObjectExecReturn("Cannot draft invalid shape"); // Faces where draft should be applied - const std::vector& SubVals = Base.getSubValuesStartsWith("Face"); + // Note: Cannot be const reference currently because of BRepOffsetAPI_DraftAngle::Remove() bug, see below + std::vector SubVals = Base.getSubValuesStartsWith("Face"); if (SubVals.size() == 0) return new App::DocumentObjectExecReturn("No faces specified"); @@ -241,7 +242,7 @@ App::DocumentObjectExecReturn *Draft::execute(void) Part::TopoShape baseShape(TopShape); baseShape.setTransform(Base::Matrix4D()); try { - BRepOffsetAPI_DraftAngle mkDraft(baseShape._Shape); + BRepOffsetAPI_DraftAngle mkDraft; // Note: // LocOpe_SplitDrafts can split a face with a wire and apply draft to both parts // Not clear though whether the face must have free boundaries @@ -250,16 +251,30 @@ App::DocumentObjectExecReturn *Draft::execute(void) // BRepFeat_MakeDPrism requires a support for the operation but will probably support multiple // wires in the sketch - for (std::vector::const_iterator it=SubVals.begin(); it != SubVals.end(); ++it) { - TopoDS_Face face = TopoDS::Face(baseShape.getSubShape(it->c_str())); - // TODO: What is the flag for? - mkDraft.Add(face, pullDirection, angle, neutralPlane); - if (!mkDraft.AddDone()) { - // Note: the function ProblematicShape returns the face on which the error occurred, - mkDraft.Remove(face); - Base::Console().Error("Adding face failed: %u\n", mkDraft.Status()); + bool success; + + do { + success = true; + mkDraft.Init(baseShape._Shape); + + for (std::vector::iterator it=SubVals.begin(); it != SubVals.end(); ++it) { + TopoDS_Face face = TopoDS::Face(baseShape.getSubShape(it->c_str())); + // TODO: What is the flag for? + mkDraft.Add(face, pullDirection, angle, neutralPlane); + if (!mkDraft.AddDone()) { + // Note: the function ProblematicShape returns the face on which the error occurred + // Note: mkDraft.Remove() stumbles on a bug in Draft_Modification::Remove() and is + // therefore unusable. See https://sourceforge.net/apps/phpbb/free-cad/viewtopic.php?f=10&t=3209&start=10#p25341 + // The only solution is to discard mkDraft and start over without the current face + // mkDraft.Remove(face); + Base::Console().Error("Adding face failed on %s. Omitted\n", it->c_str()); + success = false; + SubVals.erase(it); + break; + } } } + while (!success); mkDraft.Build(); if (!mkDraft.IsDone()) From 1b957e98bc07157855c93b3b3c4d099cca180599 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 4 Dec 2012 11:32:30 +0100 Subject: [PATCH 14/15] Fixes for OCC 6.3 --- src/Mod/PartDesign/App/FeatureDraft.cpp | 1 + src/Mod/PartDesign/App/FeatureSketchBased.cpp | 5 ++++ src/Mod/PartDesign/CMakeLists.txt | 25 ++----------------- src/Mod/PartDesign/Gui/Workbench.cpp | 5 +--- src/Mod/PartDesign/InitGui.py | 1 - 5 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index 38ed3e713..422c1a827 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -47,6 +47,7 @@ # include #endif +#include #include #include "FeatureDraft.h" diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.cpp b/src/Mod/PartDesign/App/FeatureSketchBased.cpp index 5a9aaa41c..6d2fb9b3b 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.cpp +++ b/src/Mod/PartDesign/App/FeatureSketchBased.cpp @@ -57,6 +57,7 @@ # include # include # include +# include #endif @@ -562,7 +563,11 @@ const bool SketchBased::checkLineCrossesFace(const gp_Lin &line, const TopoDS_Fa for (int i = 1; i <= intersector.NbExt(); i++) { +#if OCC_VERSION_HEX >= 0x060500 if (intersector.SquareDistance(i) < Precision::Confusion()) { +#else + if (intersector.Value(i) < Precision::Confusion()) { +#endif if (intersector.IsParallel()) { // A line that is coincident with the axis produces three intersections // 1 with the line itself and 2 with the adjacent edges diff --git a/src/Mod/PartDesign/CMakeLists.txt b/src/Mod/PartDesign/CMakeLists.txt index 72c01c533..956811364 100644 --- a/src/Mod/PartDesign/CMakeLists.txt +++ b/src/Mod/PartDesign/CMakeLists.txt @@ -44,27 +44,10 @@ SOURCE_GROUP("wizardshaft" FILES ${WizardShaft_SRCS}) SET(all_wizardshaft_files ${WizardShaft_SRCS}) ADD_CUSTOM_TARGET(WizardShaft ALL - SOURCES ${all_wizardshaft_files} + SOURCES ${all_wizardshaft_files} ) -SET(FeatureBore_SRCS - FeatureBore/__init__.py - FeatureBore/FeatureBore.py - FeatureBore/TaskBore.py -) -SOURCE_GROUP("featurebore" FILES ${FeatureBore_SRCS}) - -SET(FeatureBore_UI - FeatureBore/TaskBore.ui -) - -SET(all_featurebore_files ${FeatureBore_SRCS} ${FeatureBore_UI}) - -ADD_CUSTOM_TARGET(FeatureBore ALL - SOURCES ${all_featurebore_files} -) - -SET(all_files ${all_featurebore_files} ${all_wizardshaft_files}) +SET(all_files ${all_wizardshaft_files}) fc_copy_sources(Mod/PartDesign "${CMAKE_BINARY_DIR}/Mod/PartDesign" ${all_files}) @@ -73,8 +56,4 @@ INSTALL( ${WizardShaft_SRCS} DESTINATION Mod/PartDesign/WizardShaft - FILES - ${FeatureBore_SRCS} - DESTINATION - Mod/PartDesign/FeatureBore ) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 5d39779d1..5a11069c1 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -87,7 +87,6 @@ void Workbench::activated() "PartDesign_Fillet", "PartDesign_Chamfer", "PartDesign_Draft", - "PartDesign_Bore", 0}; Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( "SELECT Part::Feature SUBELEMENT Face COUNT 1", @@ -220,9 +219,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "PartDesign_LinearPattern" << "PartDesign_PolarPattern" // << "PartDesign_Scaled" - << "PartDesign_MultiTransform" - << "Separator" - << "PartDesign_Bore" + << "PartDesign_MultiTransform"; // For 0.13 a couple of python packages like numpy, matplotlib and others // are not deployed with the installer on Windows. Thus, the WizardShaft is // not deployed either hence the check for the existence of the command. diff --git a/src/Mod/PartDesign/InitGui.py b/src/Mod/PartDesign/InitGui.py index 6f67a02b7..8b590eafb 100644 --- a/src/Mod/PartDesign/InitGui.py +++ b/src/Mod/PartDesign/InitGui.py @@ -31,7 +31,6 @@ class PartDesignWorkbench ( Workbench ): "PartDesign workbench object" - from FeatureBore import TaskBore Icon = """ /* XPM */ static char * partdesign_xpm[] = { From 6455c4edadcae79cb58be76eb0f91359160b93d3 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 4 Dec 2012 12:21:54 +0100 Subject: [PATCH 15/15] Update draft and shaft icons --- .../Gui/Resources/icons/PartDesign_Draft.svg | 272 +++++++++--------- .../PartDesign/WizardShaft/WizardShaft.svg | 25 +- 2 files changed, 164 insertions(+), 133 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg index 6881049b5..bc7a39bec 100644 --- a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg +++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Draft.svg @@ -12,121 +12,130 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="64px" height="64px" - id="svg6248" + id="svg3364" sodipodi:version="0.32" inkscape:version="0.48.3.1 r9886" - sodipodi:docname="PartDesign_Draft.svg" + sodipodi:docname="PartDesign_DraftAngle.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" version="1.1"> + id="defs3366"> + id="linearGradient3835"> + style="stop-color:#637dca;stop-opacity:1;" /> + style="stop-color:#9eaede;stop-opacity:1;" /> - + id="linearGradient3827"> + id="stop3829" /> + id="stop3831" /> - - - - - - - - + style="stop-color:#840000;stop-opacity:0.80392158;" /> + style="stop-color:#ff2b1e;stop-opacity:0.80392158;" /> + + + + + + + + + + + + + + id="metadata3369"> image/svg+xml - + @@ -163,42 +172,45 @@ id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer"> - - - - - - + + + + + + + + diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaft.svg b/src/Mod/PartDesign/WizardShaft/WizardShaft.svg index 4f624f6c3..26b81e1e6 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaft.svg +++ b/src/Mod/PartDesign/WizardShaft/WizardShaft.svg @@ -516,6 +516,15 @@ x2="13.874617" y2="43.090275" gradientUnits="userSpaceOnUse" /> + + id="g3884"> +