/*************************************************************************** * Copyright (c) 2008 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "Placement.h" #include "ui_Placement.h" using namespace Gui::Dialog; namespace bp = boost::placeholders; namespace Gui { namespace Dialog { class find_placement { public: explicit find_placement(const std::string& name) : propertyname(name) { } bool operator () (const std::pair& elem) const { if (elem.first == propertyname) { // flag set that property is read-only or hidden if (elem.second->testStatus(App::Property::ReadOnly) || elem.second->testStatus(App::Property::Hidden)) return false; App::PropertyContainer* parent = elem.second->getContainer(); if (parent) { // flag set that property is read-only or hidden if (parent->isReadOnly(elem.second) || parent->isHidden(elem.second)) return false; } return elem.second->isDerivedFrom (Base::Type::fromName("App::PropertyPlacement")); } return false; } static App::PropertyPlacement* getProperty(App::DocumentObject* obj, const std::string& propertyName) { std::map props; obj->getPropertyMap(props); // search for the placement property std::map::iterator jt; jt = std::find_if(props.begin(), props.end(), find_placement(propertyName)); if (jt != props.end()) { return dynamic_cast(jt->second); } return nullptr; } std::string propertyname; }; } } PlacementHandler::PlacementHandler() : propertyName{"Placement"} , changeProperty{false} { } void PlacementHandler::openTransactionIfNeeded() { if (changeProperty) { openTransaction(); } } void PlacementHandler::setPropertyName(const std::string& name) { propertyName = name; // Only the Placement property it's possible to directly change the Inventor representation. // For other placement properties with a different name the standard property handling must be used. changeProperty = (propertyName != "Placement"); } const std::string& PlacementHandler::getPropertyName() const { return propertyName; } void PlacementHandler::appendDocument(const std::string& name) { documents.insert(name); } void PlacementHandler::activatedDocument(const std::string& name) { appendDocument(name); if (changeProperty) { QMetaObject::invokeMethod(this, "openTransaction", Qt::QueuedConnection); } } void PlacementHandler::openTransaction() { App::Document* activeDoc = App::GetApplication().getActiveDocument(); if (activeDoc) activeDoc->openTransaction("Placement"); } void PlacementHandler::revertTransformation() { for (const auto & it : documents) { Gui::Document* document = Application::Instance->getDocument(it.c_str()); if (document) { if (!changeProperty) { revertTransformationOfViewProviders(document); } else { document->abortCommand(); } } } } std::vector PlacementHandler::getObjects(Gui::Document* document) const { return document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId()); } std::vector PlacementHandler::getSelectedObjects(Gui::Document* document) const { return Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), document->getDocument()->getName()); } void PlacementHandler::revertTransformationOfViewProviders(Gui::Document* document) { std::vector obj = getObjects(document); for (const auto & it : obj) { auto property = find_placement::getProperty(it, this->propertyName); if (property) { Base::Placement cur = property->getValue(); Gui::ViewProvider* vp = document->getViewProvider(it); if (vp) { vp->setTransformation(cur.toMatrix()); } } } } void PlacementHandler::applyPlacement(const Base::Placement& p, bool incremental) { Gui::Document* document = Application::Instance->activeDocument(); if (!document) return; std::vector sel = getSelectedObjects(document); if (!sel.empty()) { for (const auto & it : sel) { applyPlacement(document, it, p, incremental); } } else { Base::Console().Warning("No object selected.\n"); } } void PlacementHandler::applyPlacement(Gui::Document* document, App::DocumentObject* obj, const Base::Placement& p, bool incremental) { auto property = find_placement::getProperty(obj, this->propertyName); if (property) { Base::Placement cur = property->getValue(); if (incremental) cur = p * cur; else cur = p; if (!changeProperty) { Gui::ViewProvider* vp = document->getViewProvider(obj); if (vp) { vp->setTransformation(cur.toMatrix()); } } else { property->setValue(cur); } } } void PlacementHandler::applyPlacement(const QString& data, bool incremental) { Gui::Document* document = Application::Instance->activeDocument(); if (!document) return; // When directly changing the property we now only have to commit the transaction, // do a recompute and open a new transaction if (changeProperty) { document->commitCommand(); tryRecompute(document); document->openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); } else { std::vector sel = getSelectedObjects(document); if (!sel.empty()) { document->openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); for (const auto & it : sel) { applyPlacement(it, data, incremental); } document->commitCommand(); tryRecompute(document); } else { Base::Console().Warning("No object selected.\n"); } } } void PlacementHandler::applyPlacement(App::DocumentObject* obj, const QString& data, bool incremental) { auto property = find_placement::getProperty(obj, this->propertyName); if (property) { QString cmd; if (incremental) { cmd = getIncrementalPlacement(obj, data); } else { cmd = getSimplePlacement(obj, data); } Gui::Command::runCommand(Gui::Command::App, cmd.toLatin1()); } } QString PlacementHandler::getIncrementalPlacement(App::DocumentObject* obj, const QString& data) const { return QString::fromLatin1( "App.getDocument(\"%1\").%2.%3=%4.multiply(App.getDocument(\"%1\").%2.%3)") .arg(QString::fromLatin1(obj->getDocument()->getName()), QString::fromLatin1(obj->getNameInDocument()), QString::fromLatin1(this->propertyName.c_str()), data); } QString PlacementHandler::getSimplePlacement(App::DocumentObject* obj, const QString& data) const { return QString::fromLatin1( "App.getDocument(\"%1\").%2.%3=%4") .arg(QString::fromLatin1(obj->getDocument()->getName()), QString::fromLatin1(obj->getNameInDocument()), QString::fromLatin1(this->propertyName.c_str()), data); } void PlacementHandler::tryRecompute(Gui::Document* document) { try { document->getDocument()->recompute(); } catch (...) { } } // ---------------------------------------------------------------------------- /* TRANSLATOR Gui::Dialog::Placement */ Placement::Placement(QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl) , ui{nullptr} { setupUi(); setupConnections(); setupUnits(); setupSignalMapper(); setupDocument(); setupRotationMethod(); } Placement::~Placement() { connectAct.disconnect(); delete ui; } void Placement::setupUi() { ui = new Ui_Placement(); ui->setupUi(this); ui->gridLayout->removeItem(ui->vSpacer); } void Placement::setupConnections() { connect(ui->applyButton, &QPushButton::clicked, this, &Placement::onApplyButtonClicked); connect(ui->applyIncrementalPlacement, &QCheckBox::toggled, this, &Placement::onApplyIncrementalPlacementToggled); connect(ui->resetButton, &QPushButton::clicked, this, &Placement::onResetButtonClicked); connect(ui->centerOfMass, &QCheckBox::toggled, this, &Placement::onCenterOfMassToggled); connect(ui->selectedVertex, &QPushButton::clicked, this, &Placement::onSelectedVertexClicked); connect(ui->applyAxial, &QPushButton::clicked, this, &Placement::onApplyAxialClicked); } void Placement::setupUnits() { ui->xPos->setUnit(Base::Unit::Length); ui->yPos->setUnit(Base::Unit::Length); ui->zPos->setUnit(Base::Unit::Length); ui->axialPos->setUnit(Base::Unit::Length); ui->xCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->yCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->zCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->angle->setUnit(Base::Unit::Angle); ui->yawAngle->setMaximum(180.0F); ui->yawAngle->setMinimum(-180.0F); ui->yawAngle->setUnit(Base::Unit::Angle); ui->yawAngle->checkRangeInExpression(true); ui->pitchAngle->setMaximum(90.0F); ui->pitchAngle->setMinimum(-90.0F); ui->pitchAngle->setUnit(Base::Unit::Angle); ui->pitchAngle->checkRangeInExpression(true); ui->rollAngle->setMaximum(180.0F); ui->rollAngle->setMinimum(-180.0F); ui->rollAngle->setUnit(Base::Unit::Angle); ui->rollAngle->checkRangeInExpression(true); } void Placement::setupSignalMapper() { // create a signal mapper in order to have one slot to perform the change signalMapper = new QSignalMapper(this); signalMapper->setMapping(this, 0); int id = 1; QList sb = this->findChildren(); for (const auto & it : sb) { connect(it, qOverload(&QuantitySpinBox::valueChanged), signalMapper, qOverload<>(&QSignalMapper::map)); signalMapper->setMapping(it, id++); } #if QT_VERSION < QT_VERSION_CHECK(5,15,0) connect(signalMapper, qOverload(&QSignalMapper::mapped), this, &Placement::onPlacementChanged); #else connect(signalMapper, &QSignalMapper::mappedInt, this, &Placement::onPlacementChanged); #endif } void Placement::setupDocument() { connectAct = Application::Instance->signalActiveDocument.connect (boost::bind(&Placement::slotActiveDocument, this, bp::_1)); App::Document* activeDoc = App::GetApplication().getActiveDocument(); if (activeDoc) { handler.appendDocument(activeDoc->getName()); } } void Placement::setupRotationMethod() { ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement"); long index = hGrp->GetInt("RotationMethod"); ui->rotationInput->setCurrentIndex(index); ui->stackedWidget->setCurrentIndex(index); } void Placement::showDefaultButtons(bool ok) { ui->oKButton->setVisible(ok); ui->closeButton->setVisible(ok); ui->applyButton->setVisible(ok); ui->buttonBoxLayout->invalidate(); if (ok) { ui->buttonBoxLayout->insertSpacerItem(0, ui->buttonBoxSpacer); } else { ui->buttonBoxLayout->removeItem(ui->buttonBoxSpacer); } } void Placement::open() { handler.openTransactionIfNeeded(); } void Placement::slotActiveDocument(const Gui::Document& doc) { handler.activatedDocument(doc.getDocument()->getName()); } QWidget* Placement::getInvalidInput() const { QList sb = this->findChildren(); for (const auto & it : sb) { if (!it->hasValidInput()) return it; } return nullptr; } void Placement::onPlacementChanged(int) { // If there are listeners to the 'placementChanged' signal we rely // on that the listener updates any placement. If no listeners // are connected the placement is applied to all selected objects // automatically. bool incr = ui->applyIncrementalPlacement->isChecked(); Base::Placement plm = this->getPlacement(); handler.applyPlacement(plm, incr); QVariant data = QVariant::fromValue(plm); Q_EMIT placementChanged(data, incr, false); } void Placement::onCenterOfMassToggled(bool on) { ui->xCnt->setDisabled(on); ui->yCnt->setDisabled(on); ui->zCnt->setDisabled(on); if (on) { cntOfMass = getCenterOfMass(); ui->xCnt->setValue(cntOfMass.x); ui->yCnt->setValue(cntOfMass.y); ui->zCnt->setValue(cntOfMass.z); } } void Placement::onSelectedVertexClicked() { cntOfMass.Set(0,0,0); ui->centerOfMass->setChecked(false); bool success=false; std::vector selection = Gui::Selection().getSelectionEx(); std::vector picked; //combine all pickedpoints into single vector //even if points are from separate objects Base::Vector3d firstSelected; //first selected will be central point when 3 points picked for (auto it=selection.begin(); it!=selection.end(); ++it){ std::vector points = it->getPickedPoints(); if (it==selection.begin() && !points.empty()){ firstSelected=points[0]; } picked.insert(picked.begin(),points.begin(),points.end()); } //we have to clear selection and reselect original object(s) //else later on the rotation is applied twice because there will //be 2 (vertex) objects in the selection, and even if both are subobjects //of the same object the rotation still gets applied twice Gui::Selection().clearSelection(); //reselect original object that was selected when placement dlg first opened for (const auto& it : selectionObjects) Gui::Selection().addSelection(it); if (picked.size() == 1) { ui->xCnt->setValue(picked[0].x); ui->yCnt->setValue(picked[0].y); ui->zCnt->setValue(picked[0].z); cntOfMass.x=picked[0].x; cntOfMass.y=picked[0].y; cntOfMass.z=picked[0].z; success=true; } else if (picked.size() == 2) { //average the coords to get center of rotation ui->xCnt->setValue((picked[0].x+picked[1].x)/2.0); ui->yCnt->setValue((picked[0].y+picked[1].y)/2.0); ui->zCnt->setValue((picked[0].z+picked[1].z)/2.0); cntOfMass.x=(picked[0].x+picked[1].x)/2.0; cntOfMass.y=(picked[0].y+picked[1].y)/2.0; cntOfMass.z=(picked[0].z+picked[1].z)/2.0; //setup a customized axis since the user selected 2 points //keep any existing angle, but setup our own axis Base::Placement plm = getPlacement(); Base::Rotation rot = plm.getRotation(); Base::Vector3d tmp; double angle; rot.getRawValue(tmp, angle); Base::Vector3d axis; if (firstSelected==picked[0]){ axis = Base::Vector3d(picked[1]-picked[0]); } else { axis = Base::Vector3d(picked[0]-picked[1]); } double length = axis.Length(); Base::Console().Message("Distance: %.8f\n",length); if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click QLocale loc; QApplication::clipboard()->setText(loc.toString(length,'g',8)); } else { Base::Console().Message("(Shift + click Selected points button to copy distance to clipboard)\n"); } axis.Normalize(); rot.setValue(axis, angle); plm.setRotation(rot); setPlacementData(plm); //creates custom axis, if needed ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler ui->stackedWidget->setCurrentIndex(0); success=true; } else if (picked.size() == 3){ /* User selected 3 points, so we find the plane defined by those * and use the normal vector that contains the first point picked * as the axis of rotation. */ Base::Vector3d a, b(firstSelected), c; //b is on central axis if (picked[0] == firstSelected){ a = picked[1]; c = picked[2]; } else if (picked[1]==firstSelected){ a = picked[0]; c = picked[2]; } else if (picked[2] == firstSelected){ a = picked[0]; c = picked[1]; } Base::Vector3d norm((a-b).Cross(c-b)); norm.Normalize(); ui->xCnt->setValue(b.x); ui->yCnt->setValue(b.y); ui->zCnt->setValue(b.z); cntOfMass.x=b.x; cntOfMass.y=b.y; cntOfMass.z=b.z; //setup a customized axis normal to the plane //keep any existing angle, but setup our own axis Base::Placement plm = getPlacement(); Base::Rotation rot = plm.getRotation(); Base::Vector3d tmp; double angle; rot.getRawValue(tmp, angle); double length = (a-c).Length(); Base::Console().Message("Distance: %.8f\n",length); Base::Vector3d v1(a-b); Base::Vector3d v2(c-b); v1.Normalize(); v2.Normalize(); double targetAngle = Base::toDegrees(v2.GetAngle(v1)); Base::Console().Message("Target angle: %.8f degrees, complementary: %.8f degrees\n",targetAngle, 90.0-targetAngle); if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click QLocale loc; QApplication::clipboard()->setText(loc.toString(targetAngle,'g',8)); Base::Console().Message("(Angle copied to clipboard, but you might need to use a negative (-) angle sometimes.)\n"); } else { Base::Console().Message("(Shift + click Selected points button to copy angle to clipboard)\n"); } rot.setValue(norm, angle); plm.setRotation(rot); setPlacementData(plm); //creates custom axis, if needed ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler ui->stackedWidget->setCurrentIndex(0); success=true; } if (!success){ Base::Console().Warning("Placement selection error. Select either 1 or 2 points.\n"); QMessageBox msgBox; msgBox.setText(tr("Please select 1, 2, or 3 points before clicking this button. A point may be on a vertex, \ face, or edge. If on a face or edge the point used will be the point at the mouse position along \ face or edge. If 1 point is selected it will be used as the center of rotation. If 2 points are \ selected the midpoint between them will be the center of rotation and a new custom axis will be \ created, if needed. If 3 points are selected the first point becomes the center of rotation and \ lies on the vector that is normal to the plane defined by the 3 points. Some distance and angle \ information is provided in the report view, which can be useful when aligning objects. For your \ convenience when Shift + click is used the appropriate distance or angle is copied to the clipboard.")); msgBox.exec(); ui->xCnt->setValue(0); ui->yCnt->setValue(0); ui->zCnt->setValue(0); return; } } void Placement::onApplyAxialClicked() { signalMapper->blockSignals(true); double axPos = ui->axialPos->value().getValue(); Base::Placement p = getPlacementData(); double angle; Base::Vector3d axis; p.getRotation().getValue(axis, angle); Base::Vector3d curPos (p.getPosition()); Base::Vector3d newPos; Qt::KeyboardModifiers km = QApplication::keyboardModifiers(); if (km == Qt::ShiftModifier){ //go opposite direction on Shift+click newPos = Base::Vector3d(curPos.x-(axis.x*axPos),curPos.y-(axis.y*axPos),curPos.z-(axis.z*axPos)); } else { newPos = Base::Vector3d(curPos.x+(axis.x*axPos),curPos.y+(axis.y*axPos),curPos.z+(axis.z*axPos)); } ui->xPos->setValue(Base::Quantity(newPos.x,Base::Unit::Length)); ui->yPos->setValue(Base::Quantity(newPos.y,Base::Unit::Length)); ui->zPos->setValue(Base::Quantity(newPos.z,Base::Unit::Length)); signalMapper->blockSignals(false); onPlacementChanged(0); } void Placement::onApplyIncrementalPlacementToggled(bool on) { if (on) { this->ref = getPlacementData(); onResetButtonClicked(); } else { Base::Placement p = getPlacementData(); p = p * this->ref; setPlacementData(p); onPlacementChanged(0); } } void Placement::keyPressEvent(QKeyEvent* ke) { // The placement dialog is embedded into a task panel // which is a parent widget and will handle the event ke->ignore(); } void Placement::reject() { Base::Placement plm; handler.applyPlacement(plm, true); QVariant data = QVariant::fromValue(plm); Q_EMIT placementChanged(data, true, false); handler.revertTransformation(); // One of the quantity spin boxes still can emit a signal when it has the focus // but its content is not fully updated. // In order to override again the placement the signalMapper is blocked // See related forum thread: // https://forum.freecadweb.org/viewtopic.php?f=3&t=44341#p378659 QSignalBlocker block(signalMapper); QDialog::reject(); } void Placement::accept() { if (onApply()) { handler.revertTransformation(); QDialog::accept(); } } void Placement::onApplyButtonClicked() { onApply(); } void Placement::showErrorMessage() { QMessageBox msg(this); msg.setWindowTitle(tr("Incorrect quantity")); msg.setIcon(QMessageBox::Critical); msg.setText(tr("There are input fields with incorrect input, please ensure valid placement values!")); msg.exec(); } bool Placement::onApply() { //only process things when we have valid inputs! QWidget* input = getInvalidInput(); if (input) { input->setFocus(); showErrorMessage(); return false; } // If there are listeners to the 'placementChanged' signal we rely // on that the listener updates any placement. If no listeners // are connected the placement is applied to all selected objects // automatically. bool incr = ui->applyIncrementalPlacement->isChecked(); Base::Placement plm = this->getPlacement(); handler.applyPlacement(getPlacementString(), incr); QVariant data = QVariant::fromValue(plm); Q_EMIT placementChanged(data, incr, true); if (ui->applyIncrementalPlacement->isChecked()) { QList sb = this->findChildren(); for (auto & it : sb) { it->blockSignals(true); it->setValue(0); it->blockSignals(false); } } ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement"); hGrp->SetInt("RotationMethod", ui->rotationInput->currentIndex()); return true; } void Placement::onResetButtonClicked() { QList sb = this->findChildren(); for (auto & it : sb) { it->blockSignals(true); it->setValue(0); it->blockSignals(false); } onPlacementChanged(0); } /*! * \brief Placement::setSelection * Sets the array of selection objects. * \param selection */ void Placement::setSelection(const std::vector& selection) { selectionObjects = selection; } void Placement::setPropertyName(const std::string& name) { handler.setPropertyName(name); } /*! * \brief Placement::bindObject * Binds the spin boxes to the placement components of the first object of the selection. * This requires the call of \a setSelection() beforehand. */ void Placement::bindObject() { if (!selectionObjects.empty()) { App::DocumentObject* obj = selectionObjects.front().getObject(); std::string propertyName = handler.getPropertyName(); ui->xPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.x"))); ui->yPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.y"))); ui->zPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.z"))); ui->xAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.x"))); ui->yAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.y"))); ui->zAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.z"))); ui->angle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Angle"))); ui->yawAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Yaw"))); ui->pitchAngle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Pitch"))); ui->rollAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Roll"))); ui->yawAngle->evaluateExpression(); ui->pitchAngle->evaluateExpression(); ui->rollAngle->evaluateExpression(); } } Base::Vector3d Placement::getDirection() const { double x = ui->xAxis->value().getValue(); double y = ui->yAxis->value().getValue(); double z = ui->zAxis->value().getValue(); return Base::Vector3d(x, y, z); } void Placement::setPlacement(const Base::Placement& p) { setPlacementData(p); } void Placement::setPlacementData(const Base::Placement& p) { QSignalBlocker block(signalMapper); ui->xPos->setValue(Base::Quantity(p.getPosition().x, Base::Unit::Length)); ui->yPos->setValue(Base::Quantity(p.getPosition().y, Base::Unit::Length)); ui->zPos->setValue(Base::Quantity(p.getPosition().z, Base::Unit::Length)); double Y,P,R; p.getRotation().getYawPitchRoll(Y,P,R); ui->yawAngle->setValue(Base::Quantity(Y, Base::Unit::Angle)); ui->pitchAngle->setValue(Base::Quantity(P, Base::Unit::Angle)); ui->rollAngle->setValue(Base::Quantity(R, Base::Unit::Angle)); double angle; Base::Vector3d axis; p.getRotation().getRawValue(axis, angle); ui->xAxis->setValue(axis.x); ui->yAxis->setValue(axis.y); ui->zAxis->setValue(axis.z); ui->angle->setValue(Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle)); } Base::Placement Placement::getPlacement() const { Base::Placement p = getPlacementData(); return p; } Base::Rotation Placement::getRotationData() const { Base::Rotation rot; int index = ui->rotationInput->currentIndex(); if (index == 0) { Base::Vector3d dir = getDirection(); rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),Base::toRadians(ui->angle->value().getValue())); } else if (index == 1) { // Euler angles (XY'Z'') rot.setYawPitchRoll( ui->yawAngle->value().getValue(), ui->pitchAngle->value().getValue(), ui->rollAngle->value().getValue()); } return rot; } Base::Vector3d Placement::getPositionData() const { return Base::Vector3d(ui->xPos->value().getValue(), ui->yPos->value().getValue(), ui->zPos->value().getValue()); } Base::Vector3d Placement::getAnglesData() const { return Base::Vector3d(ui->yawAngle->value().getValue(), ui->pitchAngle->value().getValue(), ui->rollAngle->value().getValue()); } Base::Vector3d Placement::getCenterData() const { if (ui->centerOfMass->isChecked()) return this->cntOfMass; return Base::Vector3d(ui->xCnt->value().getValue(), ui->yCnt->value().getValue(), ui->zCnt->value().getValue()); } Base::Vector3d Placement::getCenterOfMass() const { Base::Vector3d centerOfMass; std::vector sel = Gui::Selection().getObjectsOfType (App::GeoFeature::getClassTypeId()); if (!sel.empty()) { for (auto it : sel) { const App::PropertyComplexGeoData* propgeo = static_cast(it)->getPropertyOfGeometry(); const Data::ComplexGeoData* geodata = propgeo ? propgeo->getComplexData() : nullptr; if (geodata && geodata->getCenterOfGravity(centerOfMass)) { break; } } } return centerOfMass; } Base::Placement Placement::getPlacementData() const { Base::Rotation rot = getRotationData(); Base::Vector3d pos = getPositionData(); Base::Vector3d cnt = getCenterData(); Base::Placement plm(pos, rot, cnt); return plm; } QString Placement::getPlacementFromEulerAngles() const { Base::Vector3d pos = getPositionData(); Base::Vector3d ypr = getAnglesData(); Base::Vector3d cnt = getCenterData(); return QString::fromLatin1( "App.Placement(App.Vector(%1,%2,%3), App.Rotation(%4,%5,%6), App.Vector(%7,%8,%9))") .arg(pos.x) .arg(pos.y) .arg(pos.z) .arg(ypr.x) .arg(ypr.y) .arg(ypr.z) .arg(cnt.x) .arg(cnt.y) .arg(cnt.z); } QString Placement::getPlacementFromAxisWithAngle() const { Base::Vector3d pos = getPositionData(); Base::Vector3d cnt = getCenterData(); Base::Vector3d dir = getDirection(); double angle = ui->angle->value().getValue(); return QString::fromLatin1( "App.Placement(App.Vector(%1,%2,%3), App.Rotation(App.Vector(%4,%5,%6),%7), App.Vector(%8,%9,%10))") .arg(pos.x) .arg(pos.y) .arg(pos.z) .arg(dir.x) .arg(dir.y) .arg(dir.z) .arg(angle) .arg(cnt.x) .arg(cnt.y) .arg(cnt.z); } QString Placement::getPlacementString() const { QString cmd; int index = ui->rotationInput->currentIndex(); if (index == 0) { cmd = getPlacementFromAxisWithAngle(); } else if (index == 1) { cmd = getPlacementFromEulerAngles(); } return cmd; } void Placement::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } else { QDialog::changeEvent(e); } } // ---------------------------------------------- /* TRANSLATOR Gui::Dialog::DockablePlacement */ DockablePlacement::DockablePlacement(QWidget* parent, Qt::WindowFlags fl) : Placement(parent, fl) { Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Placement"), this, Qt::BottomDockWidgetArea); dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); dw->show(); } DockablePlacement::~DockablePlacement() { } void DockablePlacement::accept() { // closes the dock window Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); pDockMgr->removeDockWindow(this); Placement::accept(); } void DockablePlacement::reject() { // closes the dock window Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); pDockMgr->removeDockWindow(this); Placement::reject(); } // ---------------------------------------------- /* TRANSLATOR Gui::Dialog::TaskPlacement */ TaskPlacement::TaskPlacement() { this->setButtonPosition(TaskPlacement::South); widget = new Placement(); widget->showDefaultButtons(false); taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(),true, nullptr); taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); connect(widget, &Placement::placementChanged, this, &TaskPlacement::slotPlacementChanged); } TaskPlacement::~TaskPlacement() { // automatically deleted in the sub-class } /*! * \brief TaskPlacement::setSelection * Sets the array of selection objects. * \param selection */ void TaskPlacement::setSelection(const std::vector& selection) { widget->setSelection(selection); } /*! * \brief TaskPlacement::bindObject * Binds the spin boxes to the placement components of the first object of the selection. * This requires the call of \a setSelection() beforehand. */ void TaskPlacement::bindObject() { widget->bindObject(); } void TaskPlacement::open() { widget->open(); } void TaskPlacement::setPropertyName(const QString& name) { widget->setPropertyName(name.toStdString()); } QDialogButtonBox::StandardButtons TaskPlacement::getStandardButtons() const { return QDialogButtonBox::Ok| QDialogButtonBox::Cancel| QDialogButtonBox::Apply; } void TaskPlacement::setPlacement(const Base::Placement& p) { widget->setPlacement(p); } void TaskPlacement::slotPlacementChanged(const QVariant & p, bool incr, bool data) { Q_EMIT placementChanged(p, incr, data); } bool TaskPlacement::accept() { widget->accept(); return (widget->result() == QDialog::Accepted); } bool TaskPlacement::reject() { widget->reject(); return (widget->result() == QDialog::Rejected); } void TaskPlacement::clicked(int id) { if (id == QDialogButtonBox::Apply) { widget->onApplyButtonClicked(); } } #include "moc_Placement.cpp"