diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index 0922ad5ea..98782e7a2 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -101,4 +101,8 @@ PyMODINIT_FUNC init_PartDesign() PartDesign::Plane ::init(); PartDesign::Line ::init(); PartDesign::Point ::init(); + + PartDesign::Point::initHints(); + PartDesign::Line ::initHints(); + PartDesign::Plane::initHints(); } diff --git a/src/Mod/PartDesign/App/DatumFeature.cpp b/src/Mod/PartDesign/App/DatumFeature.cpp index 0d4148f59..75a95ca53 100644 --- a/src/Mod/PartDesign/App/DatumFeature.cpp +++ b/src/Mod/PartDesign/App/DatumFeature.cpp @@ -43,8 +43,11 @@ #endif +#include #include "DatumFeature.h" #include +#include +#include "Mod/Part/App/PrimitiveFeature.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -52,7 +55,6 @@ using namespace PartDesign; - PROPERTY_SOURCE_ABSTRACT(PartDesign::Datum, PartDesign::Feature) Datum::Datum(void) @@ -77,13 +79,174 @@ short Datum::mustExecute(void) const void Datum::onChanged(const App::Property* prop) { + if (prop == &References) { + refTypes.clear(); + std::vector refs = References.getValues(); + std::vector refnames = References.getSubValues(); + + for (int r = 0; r < refs.size(); r++) + refTypes.insert(getRefType(refs[r], refnames[r])); + } + PartDesign::Feature::onChanged(prop); } +// Note: We don't distinguish between e.g. datum lines and edges here +// These values are just markers so it doesn't matter that they are Part features +#define PLANE Part::Plane::getClassTypeId() +#define LINE Part::Line::getClassTypeId() +#define POINT Part::Vertex::getClassTypeId() + +const Base::Type Datum::getRefType(const App::DocumentObject* obj, const std::string& subname) +{ + Base::Type type = obj->getTypeId(); + + if ((type == App::Plane::getClassTypeId()) || (type == PartDesign::Plane::getClassTypeId())) + return PLANE; + else if (type == PartDesign::Line::getClassTypeId()) + return LINE; + else if (type == PartDesign::Point::getClassTypeId()) + return POINT; + else if (type.isDerivedFrom(Part::Feature::getClassTypeId())) { + // Note: For now, only planar references are possible + if (subname.size() > 4 && subname.substr(0,4) == "Face") + return PLANE; + else if (subname.size() > 4 && subname.substr(0,4) == "Edge") + return LINE; + else if (subname.size() > 6 && subname.substr(0,6) == "Vertex") + return POINT; + } + + throw Base::Exception("PartDesign::Datum::getRefType(): Illegal object type"); +} + +// ================================ Initialize the hints ===================== + +std::map, std::set > Point::hints = std::map, std::set >(); + +void Point::initHints() +{ + std::set DONE; + DONE.insert(PartDesign::Point::getClassTypeId()); + + std::multiset key; + std::set value; + key.insert(POINT); + hints[key] = DONE; // POINT -> DONE. Point from another point or vertex + + key.clear(); value.clear(); + key.insert(LINE); + value.insert(LINE); value.insert(PLANE); + hints[key] = value; // LINE -> LINE or PLANE + + key.clear(); value.clear(); + key.insert(LINE); key.insert(LINE); + hints[key] = DONE; // {LINE, LINE} -> DONE. Point from two lines or edges + + key.clear(); value.clear(); + key.insert(LINE); key.insert(PLANE); + hints[key] = DONE; // {LINE, PLANE} -> DONE. Point from line and plane + + key.clear(); value.clear(); + key.insert(PLANE); + value.insert(PLANE); value.insert(LINE); + hints[key] = value; // PLANE -> PLANE or LINE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); + value.insert(PLANE); + hints[key] = value; // {PLANE, PLANE} -> PLANE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); key.insert(PLANE); + hints[key] = DONE; // {PLANE, PLANE, PLANE} -> DONE. Point from three planes + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); + hints[key] = value; +} + +std::map, std::set > Line::hints = std::map, std::set >(); + +void Line::initHints() +{ + std::set DONE; + DONE.insert(PartDesign::Line::getClassTypeId()); + + std::multiset key; + std::set value; + key.insert(LINE); + hints[key] = DONE; // LINE -> DONE. Line from another line or edge + + key.clear(); value.clear(); + key.insert(POINT); + value.insert(POINT); + hints[key] = value; // POINT -> POINT + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); + hints[key] = DONE; // {POINT, POINT} -> DONE. Line from two points or vertices + + key.clear(); value.clear(); + key.insert(PLANE); + value.insert(PLANE); + hints[key] = value; // PLANE -> PLANE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); + hints[key] = DONE; // {PLANE, PLANE} -> DONE. Line from two planes or faces + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); + hints[key] = value; +} + +std::map, std::set > Plane::hints = std::map, std::set >(); + +void Plane::initHints() +{ + std::set DONE; + DONE.insert(PartDesign::Plane::getClassTypeId()); + + std::multiset key; + std::set value; + key.insert(PLANE); + hints[key] = DONE; // PLANE -> DONE. Plane from another plane or face + + key.clear(); value.clear(); + key.insert(POINT); + value.insert(POINT); value.insert(LINE); + hints[key] = value; // POINT -> POINT or LINE + + key.clear(); value.clear(); + key.insert(POINT); key.insert(LINE); + hints[key] = DONE; // {POINT, LINE} -> DONE. Plane from point/vertex and line/edge + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); + value.insert(POINT); + hints[key] = value; // {POINT, POINT} -> POINT + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); key.insert(POINT); + hints[key] = DONE; // {POINT, POINT, POINT} -> DONE. Plane from 3 points or vertices + + key.clear(); value.clear(); + key.insert(LINE); + value.insert(POINT); + hints[key] = value; // LINE -> POINT + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); + hints[key] = value; +} + +// ============================================================================ + PROPERTY_SOURCE(PartDesign::Point, PartDesign::Datum) Point::Point() -{ +{ } Point::~Point() @@ -97,8 +260,43 @@ short Point::mustExecute() const App::DocumentObjectExecReturn *Point::execute(void) { + std::set hint = getHint(); + if (!((hint.size() == 1) && (hint.find(PartDesign::Point::getClassTypeId()) != hint.end()))) + return App::DocumentObject::StdReturn; // incomplete references + + // Extract the shapes of the references + std::vector shapes; + std::vector refs = References.getValues(); + std::vector refnames = References.getSubValues(); + for (int i = 0; i < refs.size(); i++) { + if (!refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("PartDesign::Point: Invalid reference type"); + Part::Feature* feature = static_cast(refs[i]); + const TopoDS_Shape& sh = feature->Shape.getValue(); + if (sh.IsNull()) + return new App::DocumentObjectExecReturn("PartDesign::Point: Reference has NULL shape"); + if (refnames[i].empty()) { + // Datum feature or App::Plane + shapes.push_back(sh); + } else { + // Get subshape + TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); + if (subshape.IsNull()) + return new App::DocumentObjectExecReturn("PartDesign::Point: Reference has NULL subshape"); + shapes.push_back(subshape); + } + } + + // Find the point gp_Pnt point(0,0,0); - // TODO: Find the point + + if (shapes.size() == 1) { + // Point from vertex or other point + if (shapes[0].ShapeType() != TopAbs_VERTEX) + return new App::DocumentObjectExecReturn("PartDesign::Point::execute(): Internal error, unexpected ShapeType"); + TopoDS_Vertex v = TopoDS::Vertex(shapes[0]); + //point.X = v. + } BRepBuilderAPI_MakeVertex MakeVertex(point); const TopoDS_Vertex& vertex = MakeVertex.Vertex(); @@ -108,6 +306,15 @@ App::DocumentObjectExecReturn *Point::execute(void) } +const std::set Point::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} + + PROPERTY_SOURCE(PartDesign::Line, PartDesign::Datum) Line::Line() @@ -139,6 +346,15 @@ App::DocumentObjectExecReturn *Line::execute(void) } +const std::set Line::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} + + PROPERTY_SOURCE(PartDesign::Plane, PartDesign::Datum) Plane::Plane() @@ -172,3 +388,12 @@ App::DocumentObjectExecReturn *Plane::execute(void) return App::DocumentObject::StdReturn; } + + +const std::set Plane::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} diff --git a/src/Mod/PartDesign/App/DatumFeature.h b/src/Mod/PartDesign/App/DatumFeature.h index e0c26a62c..7e4f6d7a8 100644 --- a/src/Mod/PartDesign/App/DatumFeature.h +++ b/src/Mod/PartDesign/App/DatumFeature.h @@ -53,10 +53,18 @@ public: const char* getViewProviderName(void) const { return "PartDesignGui::ViewProviderDatum"; } - //@} + //@} + + virtual const std::set getHint() = 0; protected: void onChanged (const App::Property* prop); + +protected: + std::multiset refTypes; + + static const Base::Type getRefType(const App::DocumentObject* obj, const std::string& subname); + }; class PartDesignExport Point : public PartDesign::Datum @@ -73,6 +81,13 @@ public: App::DocumentObjectExecReturn *execute(void); short mustExecute() const; //@} + + static void initHints(); + const std::set getHint(); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; }; class PartDesignExport Line : public PartDesign::Datum @@ -89,6 +104,13 @@ public: App::DocumentObjectExecReturn *execute(void); short mustExecute() const; //@} + + static void initHints(); + const std::set getHint(); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; }; class PartDesignExport Plane : public PartDesign::Datum @@ -104,6 +126,13 @@ public: App::DocumentObjectExecReturn *execute(void); short mustExecute() const; //@} + + static void initHints(); + const std::set getHint(); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index bd564aad2..ca74c343c 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -433,7 +433,7 @@ void CmdPartDesignPoint::activated(int iMsg) QString refStr = getReferenceString(this); PartDesign::Body *pcActiveBody = PartDesignGui::getBody(); - openCommand("Create a datum plane"); + openCommand("Create a datum point"); doCommand(Doc,"App.activeDocument().addObject('PartDesign::Point','%s')",FeatName.c_str()); if (refStr.length() > 0) doCommand(Doc,"App.activeDocument().%s.References = %s",FeatName.c_str(),refStr.toStdString().c_str()); diff --git a/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp b/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp index d4637ae86..46e114f56 100644 --- a/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include "ReferenceSelection.h" @@ -95,9 +96,10 @@ void TaskDatumParameters::makeRefStrings(std::vector& refstrings, std:: } TaskDatumParameters::TaskDatumParameters(ViewProviderDatum *DatumView,QWidget *parent) - : TaskBox(Gui::BitmapFactory().pixmap("iconName"),tr("Datum parameters"),true, parent),DatumView(DatumView) -{ - TaskBox(Gui::BitmapFactory().pixmap((QString::fromAscii("PartDesign_") + DatumView->datumType).toAscii()), DatumView->datumType + tr(" parameters"), true, parent); + : TaskBox(Gui::BitmapFactory().pixmap((QString::fromAscii("PartDesign_") + DatumView->datumType).toAscii()), + DatumView->datumType + tr(" parameters"), true, parent), + DatumView(DatumView) +{ // we need a separate container widget to add all controls to proxy = new QWidget(this); ui = new Ui_TaskDatumParameters(); @@ -135,16 +137,18 @@ TaskDatumParameters::TaskDatumParameters(ViewProviderDatum *DatumView,QWidget *p // Get the feature data PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + std::vector refs = pcDatum->References.getValues(); std::vector refnames = pcDatum->References.getSubValues(); - std::vector refstrings; - makeRefStrings(refstrings, refnames); + //bool checked1 = pcDatum->Checked.getValue(); - std::vector vals = pcDatum->Values.getValues(); + std::vector vals = pcDatum->Values.getValues(); // Fill data into dialog elements ui->spinValue1->setValue(vals[0]); //ui->checkBox1->setChecked(checked1); + std::vector refstrings; + makeRefStrings(refstrings, refnames); ui->lineRef1->setText(refstrings[0]); ui->lineRef1->setProperty("RefName", QByteArray(refnames[0].c_str())); ui->lineRef2->setText(refstrings[1]); @@ -164,10 +168,96 @@ TaskDatumParameters::TaskDatumParameters(ViewProviderDatum *DatumView,QWidget *p updateUI(); } +const QString makeRefText(std::set hint) +{ + QString result; + for (std::set::const_iterator t = hint.begin(); t != hint.end(); t++) { + QString tText; + if (((*t) == Part::Plane::getClassTypeId()) || ((*t) == PartDesign::Plane::getClassTypeId())) + tText = QObject::tr("Plane"); + else if (((*t) == Part::Line::getClassTypeId()) || ((*t) == PartDesign::Line::getClassTypeId())) + tText = QObject::tr("Line"); + else if (((*t) == Part::Vertex::getClassTypeId()) || ((*t) == PartDesign::Point::getClassTypeId())) + tText = QObject::tr("Point"); + result += QString::fromAscii(result.size() == 0 ? "" : "/") + tText; + } + + return result; +} + void TaskDatumParameters::updateUI() { ui->checkBox1->setEnabled(false); - onButtonRef1(true); + + PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + std::vector refs = pcDatum->References.getValues(); + completed = false; + + if (refs.size() == 3) { + onButtonRef1(false); // No more references required + completed = true; + return; + } + + // Get hints for further required references + std::set hint = pcDatum->getHint(); + if (hint == std::set()) { + QMessageBox::warning(this, tr("Illegal selection"), tr("This feature cannot be created with this combination of references")); + if (refs.size() == 1) { + onButtonRef1(true); + } else if (refs.size() == 2) { + onButtonRef2(true); + } else if (refs.size() == 3) { + onButtonRef3(true); + } + return; + } + + // Enable the next reference button + if (refs.size() == 0) { + ui->buttonRef2->setEnabled(false); + ui->lineRef2->setEnabled(false); + ui->buttonRef3->setEnabled(false); + ui->lineRef3->setEnabled(false); + } else if (refs.size() == 1) { + ui->buttonRef2->setEnabled(true); + ui->lineRef2->setEnabled(true); + ui->buttonRef3->setEnabled(false); + ui->lineRef3->setEnabled(false); + } else if (refs.size() == 2) { + ui->buttonRef2->setEnabled(true); + ui->lineRef2->setEnabled(true); + ui->buttonRef3->setEnabled(true); + ui->lineRef3->setEnabled(true); + } + + QString refText = makeRefText(hint); + + // Check if we have all required references + if (refText == DatumView->datumType) { + if (refs.size() == 1) { + ui->buttonRef2->setEnabled(false); + ui->lineRef2->setEnabled(false); + ui->buttonRef3->setEnabled(false); + ui->lineRef3->setEnabled(false); + } else if (refs.size() == 2) { + ui->buttonRef3->setEnabled(false); + ui->lineRef3->setEnabled(false); + } + onButtonRef1(false); // No more references required + completed = true; + return; + } + + if (refs.size() == 0) { + onButtonRef1(true); + } else if (refs.size() == 1) { + ui->buttonRef2->setText(refText); + onButtonRef2(true); + } else if (refs.size() == 2) { + ui->buttonRef3->setText(refText); + onButtonRef3(true); + } } QLineEdit* TaskDatumParameters::getLine(const int idx) @@ -196,7 +286,7 @@ void TaskDatumParameters::onSelectionChanged(const Gui::SelectionChanges& msg) if (selObj == pcDatum) return; std::string subname = msg.pSubName; - // Remove subname for planes + // Remove subname for planes and datum features if (selObj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) || selObj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) subname = ""; @@ -224,15 +314,14 @@ void TaskDatumParameters::onSelectionChanged(const Gui::SelectionChanges& msg) line->blockSignals(false); } - // Turn off reference selection mode - onButtonRef1(false); // Note: It doesn't matter whether we call onButtonRef1 or onButtonRef2 etc. + updateUI(); } } void TaskDatumParameters::onValue1Changed(double val) { PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); - std::vector vals = pcDatum->Values.getValues(); + std::vector vals = pcDatum->Values.getValues(); vals[0] = val; pcDatum->Values.setValues(vals); pcDatum->getDocument()->recomputeFeature(pcDatum); @@ -307,30 +396,28 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) ui->lineRef2->setProperty("RefName", QByteArray(newrefnames[1].c_str())); ui->lineRef3->setText(refstrings[2]); ui->lineRef3->setProperty("RefName", QByteArray(newrefnames[2].c_str())); + updateUI(); return; } + QStringList parts = text.split(QChar::fromAscii(':')); + if (parts.length() < 2) + parts.push_back(QString::fromAscii("")); // Check whether this is the name of an App::Plane or PartDesign::Datum feature - App::DocumentObject* obj = DatumView->getObject()->getDocument()->getObject(text.toAscii()); - std::string subElement = ""; + App::DocumentObject* obj = DatumView->getObject()->getDocument()->getObject(parts[0].toAscii()); + if (obj == NULL) return; - if (obj != NULL) { - if (!obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { - if (!obj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) - return; + std::string subElement; - if (!PartDesignGui::ActivePartObject->hasFeature(obj)) - return; - } // else everything is OK (we assume a Part can only have exactly 3 App::Plane objects located at the base of the feature tree) - } else { - // This is the name of a subelement of the visible solid (Face, Edge, Vertex) - App::DocumentObject* solid = PartDesignGui::ActivePartObject->getPrevSolidFeature(); - if (solid == NULL) { - // There is no solid, so we can't select from it... + if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + // everything is OK (we assume a Part can only have exactly 3 App::Plane objects located at the base of the feature tree) + subElement = ""; + } else if (obj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) { + if (!PartDesignGui::ActivePartObject->hasFeature(obj)) return; - } - - // TODO: check validity of the text that was entered: Does it actually reference an element on the solid? + subElement = ""; + } else { + // TODO: check validity of the text that was entered: Does subElement actually reference to an element on the obj? // We must expect that "text" is the translation of "Face", "Edge" or "Vertex" followed by an ID. QString name; @@ -339,7 +426,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) std::stringstream ss; str << "^" << tr("Face") << "(\\d+)$"; - if (text.indexOf(rx) < 0) { + if (parts[1].indexOf(rx) < 0) { line->setProperty("RefName", QByteArray()); return; } else { @@ -347,7 +434,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) ss << "Face" << faceId; } str << "^" << tr("Edge") << "(\\d+)$"; - if (text.indexOf(rx) < 0) { + if (parts[1].indexOf(rx) < 0) { line->setProperty("RefName", QByteArray()); return; } else { @@ -355,7 +442,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) ss << "Edge" << lineId; } str << "^" << tr("Vertex") << "(\\d+)$"; - if (text.indexOf(rx) < 0) { + if (parts[1].indexOf(rx) < 0) { line->setProperty("RefName", QByteArray()); return; } else { @@ -377,6 +464,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) refnames.push_back(subElement.c_str()); } pcDatum->References.setValues(refs, refnames); + updateUI(); //pcDatum->getDocument()->recomputeFeature(pcDatum); } @@ -451,6 +539,7 @@ void TaskDatumParameters::changeEvent(QEvent *e) ui->lineRef1->setText(refstrings[0]); ui->lineRef2->setText(refstrings[1]); ui->lineRef3->setText(refstrings[2]); + // TODO: Translate DatumView->datumType ? ui->spinValue1->blockSignals(false); ui->checkBox1->blockSignals(false); @@ -497,6 +586,11 @@ void TaskDlgDatumParameters::clicked(int) bool TaskDlgDatumParameters::accept() { + if (!parameter->isCompleted()) { + QMessageBox::warning(parameter, tr("Not enough references"), tr("Please select further references to complete this feature")); + return false; + } + std::string name = DatumView->getObject()->getNameInDocument(); try { diff --git a/src/Mod/PartDesign/Gui/TaskDatumParameters.h b/src/Mod/PartDesign/Gui/TaskDatumParameters.h index 9936172d4..c2ea01ba5 100644 --- a/src/Mod/PartDesign/Gui/TaskDatumParameters.h +++ b/src/Mod/PartDesign/Gui/TaskDatumParameters.h @@ -56,6 +56,7 @@ public: QString getReference(const int idx) const; double getValue1(void) const; bool getCheck1(void) const; + const bool isCompleted() const { return completed; } private Q_SLOTS: void onValue1Changed(double); @@ -85,6 +86,7 @@ private: ViewProviderDatum *DatumView; int refSelectionMode; + bool completed; };