diff --git a/data/tests/PadTest.fcstd b/data/tests/PadTest.fcstd new file mode 100644 index 000000000..8a7223083 Binary files /dev/null and b/data/tests/PadTest.fcstd differ diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index ef43452df..ed936a46b 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -30,6 +30,10 @@ # include # include # include +// includes for findAllFacesCutBy() +# include +# include +# include // for Precision::Confusion() #endif @@ -139,22 +143,25 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha history.type = type; TopTools_IndexedMapOfShape newM, oldM; - TopExp::MapShapes(newS, type, newM); - TopExp::MapShapes(oldS, type, oldM); + TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type" + TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type" + // Look at all objects in the old shape and try to find the modified object in the new shape for (int i=1; i<=oldM.Extent(); i++) { bool found = false; TopTools_ListIteratorOfListOfShape it; + // Find all new objects that are a modification of the old object (e.g. a face was resized) for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { found = true; - for (int j=1; j<=newM.Extent(); j++) { + for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones! if (newM(j).IsPartner(it.Value())) { - history.shapeMap[i-1].push_back(j-1); + history.shapeMap[i-1].push_back(j-1); // adjust indices to start at zero break; } } } + // Find all new objects that were generated from an old object (e.g. a face generated from an edge) for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { found = true; for (int j=1; j<=newM.Extent(); j++) { @@ -166,10 +173,12 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha } if (!found) { + // Find all old objects that don't exist any more (e.g. a face was completely cut away) if (mkShape.IsDeleted(oldM(i))) { history.shapeMap[i-1] = std::vector(); } else { + // Mop up the rest (will this ever be reached?) for (int j=1; j<=newM.Extent(); j++) { if (newM(j).IsPartner(oldM(i))) { history.shapeMap[i-1].push_back(j-1); @@ -204,6 +213,15 @@ ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& return join; } +const TopoDS_Shape Feature::findOriginOf(const TopoDS_Shape& reference) { +/* Base::Console().Error("Looking for origin of face in %s\n", this->getName()); + if (reference.ShapeType() == TopAbs_FACE) { + // Find index of reference in the history + } +*/ + return TopoDS_Shape(); +} + /// returns the type name of the ViewProvider const char* Feature::getViewProviderName(void) const { return "PartGui::ViewProviderPart"; @@ -253,3 +271,51 @@ template<> PyObject* Part::FeaturePython::getPyObject(void) { template class PartExport FeaturePythonT; } +// ---------------------------------------------------------------- +#include +#include +#include +#include +#include +#include + +std::vector Part::findAllFacesCutBy( + const TopoDS_Shape& shape, const TopoDS_Shape& face, const gp_Dir& dir) +{ + // Find the centre of gravity of the face + GProp_GProps props; + BRepGProp::SurfaceProperties(face,props); + gp_Pnt cog = props.CentreOfMass(); + + // create a line through the centre of gravity + gp_Lin line = gce_MakeLin(cog, dir); + + // Find intersection of line with all faces of the shape + std::vector result; + BRepIntCurveSurface_Inter mkSection; + // TODO: Less precision than Confusion() should be OK? + + for (mkSection.Init(shape, line, Precision::Confusion()); mkSection.More(); mkSection.Next()) { + gp_Pnt iPnt = mkSection.Pnt(); + double dsq = cog.SquareDistance(iPnt); + + if (dsq < Precision::Confusion()) + continue; // intersection with original face + + // Find out which side of the original face the intersection is on + gce_MakeDir mkDir(cog, iPnt); + if (!mkDir.IsDone()) + continue; // some error (appears highly unlikely to happen, though...) + + if (mkDir.Value().IsOpposite(dir, Precision::Confusion())) + continue; // wrong side of face (opposite to extrusion direction) + + cutFaces newF; + newF.face = mkSection.Face(); + newF.distsq = dsq; + result.push_back(newF); + } + + return result; +} +// -------------------------------------------------------------------- diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 689736a42..ed3de3737 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -29,9 +29,16 @@ #include #include #include +// includes for findAllFacesCutBy() +#include +class gp_Dir; class BRepBuilderAPI_MakeShape; +// includes for findAllFacesCutBy() +#include +class gp_Dir; + namespace Part { @@ -63,9 +70,22 @@ public: virtual PyObject* getPyObject(void); virtual std::vector getPySubObjects(const std::vector&) const; + /** + /* Find the origin of a reference, e.g. the vertex or edge in a sketch that + /* produced a face + */ + const TopoDS_Shape findOriginOf(const TopoDS_Shape& reference); + protected: void onChanged(const App::Property* prop); TopLoc_Location getLocation() const; + /** + /* Build a history of changes + /* MakeShape: The operation that created the changes, e.g. BRepAlgoAPI_Common + /* type: The type of object we are interested in, e.g. TopAbs_FACE + /* newS: The new shape that was created by the operation + /* oldS: The original shape prior to the operation + */ ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, const TopoDS_Shape& newS, const TopoDS_Shape& oldS); ShapeHistory joinHistory(const ShapeHistory&, const ShapeHistory&); @@ -99,6 +119,18 @@ public: } }; +// Utility methods +/** +/* Find all faces cut by a line through the centre of gravity of a given face +/* Useful for the "up to face" options to pocket or pad +*/ +struct cutFaces { + TopoDS_Face face; + double distsq; +}; +std::vector findAllFacesCutBy(const TopoDS_Shape& shape, + const TopoDS_Shape& face, const gp_Dir& dir); + } //namespace Part diff --git a/src/Mod/PartDesign/App/FeaturePad.cpp b/src/Mod/PartDesign/App/FeaturePad.cpp index 1a12c4556..c7742c1f4 100644 --- a/src/Mod/PartDesign/App/FeaturePad.cpp +++ b/src/Mod/PartDesign/App/FeaturePad.cpp @@ -39,23 +39,32 @@ # include # include # include +# include +# include #endif #include #include +#include #include "FeaturePad.h" using namespace PartDesign; +const char* Pad::TypeEnums[]= {"Length","UpToLast","UpToFirst","UpToFace","TwoLengths",NULL}; + PROPERTY_SOURCE(PartDesign::Pad, PartDesign::Additive) Pad::Pad() { + ADD_PROPERTY(Type,((long)0)); + Type.setEnums(TypeEnums); ADD_PROPERTY(Length,(100.0)); ADD_PROPERTY(Reversed,(0)); - ADD_PROPERTY(MirroredExtent,(0)); + ADD_PROPERTY(Midplane,(0)); + ADD_PROPERTY(Length2,(100.0)); + ADD_PROPERTY(FaceName,("")); } short Pad::mustExecute() const @@ -63,8 +72,10 @@ short Pad::mustExecute() const if (Placement.isTouched() || Sketch.isTouched() || Length.isTouched() || - MirroredExtent.isTouched() || - Reversed.isTouched()) + Midplane.isTouched() || + Reversed.isTouched() || + Length2.isTouched() || + FaceName.isTouched()) return 1; return 0; } @@ -74,6 +85,10 @@ App::DocumentObjectExecReturn *Pad::execute(void) double L = Length.getValue(); if (L < Precision::Confusion()) return new App::DocumentObjectExecReturn("Length of pad too small"); + double L2 = Length2.getValue(); + if ((std::string(Type.getValueAsString()) == "TwoLengths") && (L < Precision::Confusion())) + return new App::DocumentObjectExecReturn("Second length of pad too small"); + App::DocumentObject* link = Sketch.getValue(); if (!link) return new App::DocumentObjectExecReturn("No sketch linked"); @@ -103,10 +118,8 @@ App::DocumentObjectExecReturn *Pad::execute(void) // get the Sketch plane Base::Placement SketchPos = static_cast(link)->Placement.getValue(); Base::Rotation SketchOrientation = SketchPos.getRotation(); - Base::Vector3d SketchOrientationVector(0,0,1); - if (Reversed.getValue()) // negative direction - SketchOrientationVector *= -1; - SketchOrientation.multVec(SketchOrientationVector,SketchOrientationVector); + Base::Vector3d SketchVector(0,0,1); + SketchOrientation.multVec(SketchVector,SketchVector); // get the support of the Sketch if any App::DocumentObject* SupportLink = static_cast(link)->Support.getValue(); @@ -118,27 +131,159 @@ App::DocumentObjectExecReturn *Pad::execute(void) if (aFace.IsNull()) return new App::DocumentObjectExecReturn("Creating a face from sketch failed"); - // lengthen the vector - SketchOrientationVector *= L; - this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); try { // extrude the face to a solid - gp_Vec vec(SketchOrientationVector.x,SketchOrientationVector.y,SketchOrientationVector.z); - vec.Transform(invObjLoc.Transformation()); - BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); - if (PrismMaker.IsDone()) { - // if the sketch has a support fuse them to get one result object (PAD!) - if (SupportObject) { - // At this point the prism can be a compound - TopoDS_Shape result = PrismMaker.Shape(); - // set the additive shape property for later usage in e.g. pattern - this->AddShape.setValue(result); + TopoDS_Shape prism; + bool isSolid = false; // support is a solid? + bool isSolidChecked = false; // not checked yet + + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst") || + (std::string(Type.getValueAsString()) == "UpToFace")) + { + TopoDS_Face upToFace; + gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); + + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst")) + { + // Check for valid support object + if (!SupportObject) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No support in Sketch!"); const TopoDS_Shape& support = SupportObject->Shape.getValue(); - bool isSolid = false; + if (support.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Support shape is invalid"); + TopExp_Explorer xp (support, TopAbs_SOLID); + if (!xp.More()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Support shape is not a solid"); + isSolid = true; + isSolidChecked = true; + + TopoDS_Shape origFace = makeFace(wires); // original sketch face before moving one unit + std::vector cfaces = Part::findAllFacesCutBy(support, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction"); + + // Find nearest/furthest face + std::vector::const_iterator it, it_near, it_far; + it_near = it_far = cfaces.begin(); + for (it = cfaces.begin(); it != cfaces.end(); it++) + if (it->distsq > it_far->distsq) + it_far = it; + else if (it->distsq < it_near->distsq) + it_near = it; + upToFace = (std::string(Type.getValueAsString()) == "UpToLast" ? it_far->face : it_near->face); + } else { + if (FaceName.getValue() == "") + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No face selected"); + + // Get active object, this is the object that the user referenced when he clicked on the face! + App::DocumentObject* baseLink = this->getDocument()->getActiveObject(); + + if (!baseLink) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No object linked"); + if (!baseLink->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Linked object is not a Part object"); + Part::Feature *base = static_cast(baseLink); + const Part::TopoShape& baseShape = base->Shape.getShape(); + if (baseShape._Shape.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Cannot work on invalid shape"); + + TopoDS_Shape sub = baseShape.getSubShape(FaceName.getValue()); + if (!sub.IsNull() && sub.ShapeType() == TopAbs_FACE) + upToFace = TopoDS::Face(sub); + else + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Selection is not a face"); + + // Validate face + // TODO: This would also exclude faces that are valid but not cut by the line + // So for now we trust to the intelligence of the user when picking the face + /*std::vector cfaces = findAllFacesCutBy(upToFace, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction");*/ + } + + // Create semi-infinite prism from sketch in direction dir + // Hack, because the two lines commented out below do NOT work!!! + SketchVector *= 1E6; + gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); + vec.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); // very long, but finite prism + //dir.Transform(invObjLoc.Transformation()); + //BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),dir,0,0,1); + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Could not extrude the sketch!"); + + // Cut off the prism at the face we found + // Grab any point from the sketch + TopExp_Explorer exp; + exp.Init(aFace, TopAbs_VERTEX); + if (!exp.More()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Sketch without points?"); + gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); + + // Create a halfspace from the face, extending in direction of sketch plane + BRepPrimAPI_MakeHalfSpace mkHalfSpace(upToFace, aPnt); + if (!mkHalfSpace.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: HalfSpace creation failed"); + + // Find common material between halfspace and prism + BRepAlgoAPI_Common mkCommon(PrismMaker.Shape(), mkHalfSpace.Solid().Moved(invObjLoc)); + if (!mkCommon.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Common creation failed"); + + prism = this->getSolid(mkCommon.Shape()); + if (prism.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Resulting shape is not a solid"); + } else if ((std::string(Type.getValueAsString()) == "Length") || + (std::string(Type.getValueAsString()) == "TwoLengths")) { + if (std::string(Type.getValueAsString()) == "Length") { + if (Midplane.getValue()) { + // Move face by half the extrusion distance to get pad symmetric to sketch plane + gp_Trsf mov; + mov.SetTranslation(gp_Vec(SketchVector.x,SketchVector.y,SketchVector.z) * (-1.0) * L/2.0); + TopLoc_Location loc(mov); + aFace.Move(loc); + } else if (Reversed.getValue()) { // negative direction + SketchVector *= -1.0; + } + + // lengthen the vector + SketchVector *= L; + } else { + // Move face by the second length to get pad extending to both sides of sketch plane + gp_Trsf mov; + mov.SetTranslation(gp_Vec(SketchVector.x,SketchVector.y,SketchVector.z) * (-1.0) * L2); + TopLoc_Location loc(mov); + aFace.Move(loc); + + // lengthen the vector + SketchVector *= (L + L2); + } + + // create the extrusion + gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); + vec.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); // finite prism + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); + prism = PrismMaker.Shape(); + } else { + return new App::DocumentObjectExecReturn("Internal error: Unknown type for Pad feature"); + } + + // if the sketch has a support fuse them to get one result object (PAD!) + if (SupportObject) { + // set the additive shape property for later usage in e.g. pattern + this->AddShape.setValue(prism); + + const TopoDS_Shape& support = SupportObject->Shape.getValue(); + + if (!isSolidChecked) { // we haven't checked for solid, yet if (!support.IsNull()) { TopExp_Explorer xp; xp.Init(support,TopAbs_SOLID); @@ -147,32 +292,30 @@ App::DocumentObjectExecReturn *Pad::execute(void) break; } } - if (isSolid) { - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), result); - // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - return new App::DocumentObjectExecReturn("Fusion with support failed"); - result = mkFuse.Shape(); - // we have to get the solids (fuse create seldomly compounds) - TopoDS_Shape solRes = this->getSolid(result); - // lets check if the result is a solid - if (solRes.IsNull()) - return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); - this->Shape.setValue(solRes); - } - else + + if (!isSolid) return new App::DocumentObjectExecReturn("Support is not a solid"); } - else { - TopoDS_Shape result = this->getSolid(PrismMaker.Shape()); - // set the additive shape property for later usage in e.g. pattern - this->AddShape.setValue(result); - this->Shape.setValue(result); - } + + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), prism); + // Let's check if the fusion has been successful + if (!mkFuse.IsDone()) + return new App::DocumentObjectExecReturn("Fusion with support failed"); + TopoDS_Shape result = mkFuse.Shape(); + // we have to get the solids (fuse create seldomly compounds) + TopoDS_Shape solRes = this->getSolid(result); + // lets check if the result is a solid + if (solRes.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + this->Shape.setValue(solRes); + } + else { + TopoDS_Shape result = this->getSolid(prism); + // set the additive shape property for later usage in e.g. pattern + this->AddShape.setValue(result); + this->Shape.setValue(result); } - else - return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); return App::DocumentObject::StdReturn; } diff --git a/src/Mod/PartDesign/App/FeaturePad.h b/src/Mod/PartDesign/App/FeaturePad.h index c758c94f7..91bdeff59 100644 --- a/src/Mod/PartDesign/App/FeaturePad.h +++ b/src/Mod/PartDesign/App/FeaturePad.h @@ -38,10 +38,13 @@ class Pad : public Additive public: Pad(); - App::PropertyLength Length; + App::PropertyEnumeration Type; + App::PropertyLength Length; //App::PropertyEnumeration Side; - App::PropertyBool Reversed; - App::PropertyBool MirroredExtent; + App::PropertyBool Reversed; + App::PropertyBool Midplane; + App::PropertyLength Length2; + App::PropertyString FaceName; /** @name methods override feature */ //@{ @@ -54,7 +57,8 @@ public: } //@} private: - static const char* SideEnums[]; + static const char* TypeEnums[]; + //static const char* SideEnums[]; }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp index 8a902b2b9..d7c37b7b3 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp @@ -59,30 +59,106 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView,QWidget *parent) connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onLengthChanged(double))); - connect(ui->checkBoxMirrored, SIGNAL(toggled(bool)), - this, SLOT(onMirrored(bool))); + connect(ui->checkBoxMidplane, SIGNAL(toggled(bool)), + this, SLOT(onMidplane(bool))); connect(ui->checkBoxReversed, SIGNAL(toggled(bool)), this, SLOT(onReversed(bool))); + connect(ui->doubleSpinBox2, SIGNAL(valueChanged(double)), + this, SLOT(onLength2Changed(double))); + connect(ui->changeMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(onModeChanged(int))); + connect(ui->lineFaceName, SIGNAL(textEdited(QString)), + this, SLOT(onFaceName(QString))); this->groupLayout()->addWidget(proxy); + // Get the feature data PartDesign::Pad* pcPad = static_cast(PadView->getObject()); double l = pcPad->Length.getValue(); - bool mirrored = pcPad->MirroredExtent.getValue(); + bool midplane = pcPad->Midplane.getValue(); bool reversed = pcPad->Reversed.getValue(); + double l2 = pcPad->Length2.getValue(); + int index = pcPad->Type.getValue(); // must extract value here, clear() kills it! + const char* upToFace = pcPad->FaceName.getValue(); + // Fill data into dialog elements ui->doubleSpinBox->setMinimum(0); + ui->doubleSpinBox->setMaximum(INT_MAX); ui->doubleSpinBox->setValue(l); - ui->doubleSpinBox->selectAll(); - ui->checkBoxMirrored->setChecked(mirrored); + ui->doubleSpinBox2->setMinimum(0); + ui->doubleSpinBox2->setMaximum(INT_MAX); + ui->doubleSpinBox2->setValue(l2); + ui->checkBoxMidplane->setChecked(midplane); // According to bug #0000521 the reversed option // shouldn't be de-activated if the pad has a support face ui->checkBoxReversed->setChecked(reversed); + ui->lineFaceName->setText(upToFace == "" ? tr("No face selected") : tr(upToFace)); + ui->changeMode->clear(); + ui->changeMode->insertItem(0, tr("Dimension")); + ui->changeMode->insertItem(1, tr("To last")); + ui->changeMode->insertItem(2, tr("To first")); + ui->changeMode->insertItem(3, tr("Up to face")); + ui->changeMode->insertItem(4, tr("Two dimensions")); + ui->changeMode->setCurrentIndex(index); - // Make sure that the spin box has the focus to get key events - // Calling setFocus() directly doesn't work because the spin box is not - // yet visible. - QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + // activate and de-activate dialog elements as appropriate + updateUI(index); +} + +void TaskPadParameters::updateUI(int index) +{ + if (index == 0) { // dimension + ui->doubleSpinBox->setEnabled(true); + ui->doubleSpinBox->selectAll(); + // Make sure that the spin box has the focus to get key events + // Calling setFocus() directly doesn't work because the spin box is not + // yet visible. + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->checkBoxMidplane->setEnabled(true); + ui->checkBoxReversed->setEnabled(true); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } else if ((index == 1) || (index == 2)) { // up to first/last + ui->doubleSpinBox->setEnabled(false); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } else if (index == 3) { // up to face + ui->doubleSpinBox->setEnabled(false); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(true); + QMetaObject::invokeMethod(ui->lineFaceName, "setFocus", Qt::QueuedConnection); + } else { // two dimensions + ui->doubleSpinBox->setEnabled(true); + ui->doubleSpinBox->selectAll(); + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(true); + ui->lineFaceName->setEnabled(false); + } +} + +void TaskPadParameters::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + if (pcPad->Type.getValue() != 3) // ignore user selections if mode is not upToFace + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string element(msg.pSubName); + if (element.substr(0,4) != "Face") + return; + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + pcPad->FaceName.setValue(element); + pcPad->getDocument()->recomputeFeature(pcPad); + ui->lineFaceName->setText(tr(element.c_str())); + } } void TaskPadParameters::onLengthChanged(double len) @@ -92,10 +168,10 @@ void TaskPadParameters::onLengthChanged(double len) pcPad->getDocument()->recomputeFeature(pcPad); } -void TaskPadParameters::onMirrored(bool on) +void TaskPadParameters::onMidplane(bool on) { PartDesign::Pad* pcPad = static_cast(PadView->getObject()); - pcPad->MirroredExtent.setValue(on); + pcPad->Midplane.setValue(on); pcPad->getDocument()->recomputeFeature(pcPad); } @@ -106,6 +182,40 @@ void TaskPadParameters::onReversed(bool on) pcPad->getDocument()->recomputeFeature(pcPad); } +void TaskPadParameters::onLength2Changed(double len) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + pcPad->Length2.setValue((float)len); + pcPad->getDocument()->recomputeFeature(pcPad); +} + +void TaskPadParameters::onModeChanged(int index) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + + switch (index) { + case 0: pcPad->Type.setValue("Length"); break; + case 1: pcPad->Type.setValue("UpToLast"); break; + case 2: pcPad->Type.setValue("UpToFirst"); break; + case 3: pcPad->Type.setValue("UpToFace"); break; + default: pcPad->Type.setValue("TwoLengths"); + } + + updateUI(index); + + pcPad->getDocument()->recomputeFeature(pcPad); +} + +void TaskPadParameters::onFaceName(const QString& text) +{ + if (text.left(4) != tr("Face")) + return; + + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + pcPad->FaceName.setValue(text.toUtf8()); + pcPad->getDocument()->recomputeFeature(pcPad); +} + double TaskPadParameters::getLength(void) const { return ui->doubleSpinBox->value(); @@ -116,9 +226,24 @@ bool TaskPadParameters::getReversed(void) const return ui->checkBoxReversed->isChecked(); } -bool TaskPadParameters::getMirroredExtent(void) const +bool TaskPadParameters::getMidplane(void) const { - return ui->checkBoxMirrored->isChecked(); + return ui->checkBoxMidplane->isChecked(); +} + +double TaskPadParameters::getLength2(void) const +{ + return ui->doubleSpinBox2->value(); +} + +int TaskPadParameters::getMode(void) const +{ + return ui->changeMode->currentIndex(); +} + +const QString TaskPadParameters::getFaceName(void) const +{ + return ui->lineFaceName->text(); } TaskPadParameters::~TaskPadParameters() @@ -174,7 +299,10 @@ bool TaskDlgPadParameters::accept() //Gui::Command::openCommand("Pad changed"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Length = %f",name.c_str(),parameter->getLength()); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %i",name.c_str(),parameter->getReversed()?1:0); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.MirroredExtent = %i",name.c_str(),parameter->getMirroredExtent()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Midplane = %i",name.c_str(),parameter->getMidplane()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Length2 = %f",name.c_str(),parameter->getLength2()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Type = %u",name.c_str(),parameter->getMode()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.FaceName = \"%s\"",name.c_str(),parameter->getFaceName().toAscii().data()); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); if (!PadView->getObject()->isValid()) throw Base::Exception(PadView->getObject()->getStatusString()); diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.h b/src/Mod/PartDesign/Gui/TaskPadParameters.h index c16493cad..53afdacd0 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.h +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.h @@ -44,7 +44,7 @@ namespace PartDesignGui { -class TaskPadParameters : public Gui::TaskView::TaskBox +class TaskPadParameters : public Gui::TaskView::TaskBox, public Gui::SelectionObserver { Q_OBJECT @@ -52,19 +52,27 @@ public: TaskPadParameters(ViewProviderPad *PadView,QWidget *parent = 0); ~TaskPadParameters(); + int getMode(void) const; double getLength(void) const; + double getLength2(void) const; bool getReversed(void) const; - bool getMirroredExtent(void) const; + bool getMidplane(void) const; + const QString getFaceName(void) const; private Q_SLOTS: void onLengthChanged(double); - void onMirrored(bool); + void onMidplane(bool); void onReversed(bool); + void onLength2Changed(double); + void onModeChanged(int); + void onFaceName(const QString& text); protected: void changeEvent(QEvent *e); private: + void onSelectionChanged(const Gui::SelectionChanges& msg); + void updateUI(int index); private: QWidget* proxy; diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.ui b/src/Mod/PartDesign/Gui/TaskPadParameters.ui index d407bbf31..d1f2601cb 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.ui @@ -6,8 +6,8 @@ 0 0 - 158 - 116 + 272 + 238 @@ -19,7 +19,7 @@ - Type: + Type @@ -39,7 +39,7 @@ - Length: + Length @@ -55,19 +55,19 @@ 5.000000000000000 - 20.000000000000000 + 10.000000000000000 - + - false + true - Mirrored extent + Symmetric to plane @@ -78,6 +78,47 @@ + + + + + + 2nd length + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + 5.000000000000000 + + + 0.000000000000000 + + + + + + + + + + + Face + + + + + + + +