[Part::Mirroring] Add support for mirror plane reference object
This commit is contained in:
@@ -23,13 +23,28 @@
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <BRepBuilderAPI_Transform.hxx>
|
||||
# include <BRep_Tool.hxx>
|
||||
# include <gp_Ax2.hxx>
|
||||
# include <gp_Circ.hxx>
|
||||
# include <gp_Dir.hxx>
|
||||
# include <gp_Pnt.hxx>
|
||||
# include <gp_Trsf.hxx>
|
||||
# include <BRepAdaptor_Curve.hxx>
|
||||
# include <BRepAdaptor_Surface.hxx>
|
||||
# include <gp_Pln.hxx>
|
||||
# include <Geom_Plane.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <TopoDS_Face.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
#endif
|
||||
|
||||
#include <Mod/Part/App/PrimitiveFeature.h>
|
||||
#include <App/Link.h>
|
||||
#include <App/OriginFeature.h>
|
||||
|
||||
#include "FeatureMirroring.h"
|
||||
#include "DatumFeature.h"
|
||||
|
||||
|
||||
|
||||
using namespace Part;
|
||||
@@ -41,6 +56,7 @@ Mirroring::Mirroring()
|
||||
ADD_PROPERTY(Source,(nullptr));
|
||||
ADD_PROPERTY_TYPE(Base,(Base::Vector3d()),"Plane",App::Prop_None,"The base point of the plane");
|
||||
ADD_PROPERTY_TYPE(Normal,(Base::Vector3d(0,0,1)),"Plane",App::Prop_None,"The normal of the plane");
|
||||
ADD_PROPERTY_TYPE(MirrorPlane,(nullptr),"Plane",App::Prop_None,"A reference for the mirroring plane, overrides Base and Normal if set, can be face or circle");
|
||||
}
|
||||
|
||||
short Mirroring::mustExecute() const
|
||||
@@ -51,13 +67,39 @@ short Mirroring::mustExecute() const
|
||||
return 1;
|
||||
if (Normal.isTouched())
|
||||
return 1;
|
||||
if (MirrorPlane.isTouched())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Mirroring::onChanged(const App::Property* prop)
|
||||
{
|
||||
/**
|
||||
In the case the user has a reference plane object, then
|
||||
Base and Normal are computed based on that object. We must
|
||||
handle this by setting the changes to Base and Normal to not
|
||||
trigger a recompute when they are computed and changed. We
|
||||
should also set Base and Normal to readonly so not to confuse
|
||||
the user, who might try to change them despite having a reference
|
||||
object. We could also hide them, but they contain useful information.
|
||||
*/
|
||||
if (!isRestoring()) {
|
||||
if (prop == &Base || prop == &Normal) {
|
||||
bool needsRecompute = false;
|
||||
App::DocumentObject* refObject = MirrorPlane.getValue();
|
||||
if (!refObject){
|
||||
Base.setStatus(App::Property::ReadOnly, false);
|
||||
Normal.setStatus(App::Property::ReadOnly, false);
|
||||
if (prop == &Base || prop == &Normal) {
|
||||
needsRecompute = true;
|
||||
}
|
||||
} else {
|
||||
if (prop == &MirrorPlane){
|
||||
Base.setStatus(App::Property::ReadOnly, true);
|
||||
Normal.setStatus(App::Property::ReadOnly, true);
|
||||
needsRecompute = true;
|
||||
}
|
||||
}
|
||||
if (needsRecompute){
|
||||
try {
|
||||
App::DocumentObjectExecReturn *ret = recompute();
|
||||
delete ret;
|
||||
@@ -95,13 +137,124 @@ App::DocumentObjectExecReturn *Mirroring::execute()
|
||||
App::DocumentObject* link = Source.getValue();
|
||||
if (!link)
|
||||
return new App::DocumentObjectExecReturn("No object linked");
|
||||
|
||||
App::DocumentObject* refObject = MirrorPlane.getValue();
|
||||
|
||||
std::vector<std::string> subStrings = MirrorPlane.getSubValues();
|
||||
|
||||
gp_Pnt axbase;
|
||||
gp_Dir axdir;
|
||||
/**
|
||||
Support mirror plane reference objects:
|
||||
DatumPlanes, Part::Planes, Origin planes, Faces, Circles
|
||||
Can also be App::Links to such objects
|
||||
*/
|
||||
if (refObject){
|
||||
if (refObject->isDerivedFrom(Part::Plane::getClassTypeId()) || refObject->isDerivedFrom<App::Plane>() || (strstr(refObject->getNameInDocument(), "Plane")
|
||||
&& refObject->isDerivedFrom(Part::Datum::getClassTypeId()))) {
|
||||
Part::Feature* plane = static_cast<Part::Feature*>(refObject);
|
||||
Base::Vector3d base = plane->Placement.getValue().getPosition();
|
||||
axbase = gp_Pnt(base.x, base.y, base.z);
|
||||
Base::Rotation rot = plane->Placement.getValue().getRotation();
|
||||
Base::Vector3d dir;
|
||||
rot.multVec(Base::Vector3d(0,0,1), dir);
|
||||
axdir = gp_Dir(dir.x, dir.y, dir.z);
|
||||
// reference is an app::link or a part::feature or some subobject
|
||||
} else if (refObject->isDerivedFrom<Part::Feature>() || refObject->isDerivedFrom<App::Link>()) {
|
||||
if (subStrings.size() > 1){
|
||||
throw Base::ValueError(std::string(this->getFullLabel()) + ": Only 1 subobject is supported for Mirror Plane reference, either a plane face or a circle edge.");
|
||||
|
||||
}
|
||||
auto linked = MirrorPlane.getValue();
|
||||
bool isFace = false; //will be true if user selected face subobject or if object only has 1 face
|
||||
bool isEdge = false; //will be true if user selected edge subobject or if object only has 1 edge
|
||||
TopoDS_Shape shape;
|
||||
if (!subStrings.empty() && subStrings[0].length() > 0){
|
||||
shape = Feature::getTopoShape(linked, subStrings[0].c_str(), true).getShape();
|
||||
if (strstr(subStrings[0].c_str(), "Face")){
|
||||
isFace = true; //was face subobject, e.g. Face3
|
||||
} else {
|
||||
if (strstr(subStrings[0].c_str(), "Edge")){
|
||||
isEdge = true; //was edge subobject, e.g. Edge7
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shape = Feature::getShape(linked); //no subobjects were selected, so this is entire shape of feature
|
||||
}
|
||||
|
||||
// if there is only 1 face or 1 edge, then we don't need to force the user to select that face or edge
|
||||
// instead we can infer what was intended
|
||||
int faceCount = Part::TopoShape(shape).countSubShapes(TopAbs_FACE);
|
||||
int edgeCount = Part::TopoShape(shape).countSubShapes(TopAbs_EDGE);
|
||||
|
||||
TopoDS_Face face;
|
||||
TopoDS_Edge edge;
|
||||
|
||||
if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
|
||||
face = TopoDS::Face(shape);
|
||||
} else {
|
||||
if (faceCount == 1) { //entire feature selected, but it only has 1 face, so get that face
|
||||
TopoDS_Shape tdface = Part::TopoShape(shape).getSubShape(std::string("Face1").c_str());
|
||||
face = TopoDS::Face(tdface);
|
||||
isFace = true;
|
||||
}
|
||||
}
|
||||
if (!isFace && isEdge){ //don't bother with edge if we already have a face to work with
|
||||
edge = TopoDS::Edge(shape); //isEdge means an edge was selected
|
||||
} else {
|
||||
if (edgeCount == 1){ //we don't have a face yet and there were no edges in the subobject selection
|
||||
//but since this object only has 1 edge, we use it
|
||||
TopoDS_Shape tdedge = Part::TopoShape(shape).getSubShape(std::string("Edge1").c_str());
|
||||
edge = TopoDS::Edge(tdedge);
|
||||
isEdge = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFace && face.IsNull()) { //ensure we have a good face to work with
|
||||
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane because face is null");
|
||||
}
|
||||
if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with
|
||||
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane because edge is null");
|
||||
}
|
||||
if (!isFace && !isEdge){
|
||||
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane, unable to determine which face or edge to use.");
|
||||
}
|
||||
|
||||
if (isFace) {
|
||||
BRepAdaptor_Surface adapt(face);
|
||||
if (adapt.GetType() != GeomAbs_Plane)
|
||||
throw Base::TypeError(std::string(this->getFullLabel()) + ": Mirror plane face must be planar");
|
||||
TopExp_Explorer exp;
|
||||
exp.Init(face, TopAbs_VERTEX);
|
||||
if (exp.More()) {
|
||||
axbase = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()));
|
||||
}
|
||||
axdir = adapt.Plane().Axis().Direction();
|
||||
} else {
|
||||
if (isEdge){
|
||||
BRepAdaptor_Curve curve(edge);
|
||||
if (!(curve.GetType() == GeomAbs_Circle)) {
|
||||
throw Base::TypeError(std::string(this->getFullLabel()) + ": Only circle edge types are supported");
|
||||
}
|
||||
gp_Circ circle = curve.Circle();
|
||||
axdir = circle.Axis().Direction();
|
||||
axbase = circle.Location();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Base::ValueError(std::string(this->getFullLabel()) + ": Mirror plane reference must be a face of a feature or a plane object or a circle");
|
||||
}
|
||||
Base.setValue(axbase.X(), axbase.Y(), axbase.Z());
|
||||
Normal.setValue(axdir.X(), axdir.Y(), axdir.Z());
|
||||
}
|
||||
|
||||
Base::Vector3d base = Base.getValue();
|
||||
Base::Vector3d norm = Normal.getValue();
|
||||
|
||||
try {
|
||||
const TopoDS_Shape& shape = Feature::getShape(link);
|
||||
if (shape.IsNull())
|
||||
Standard_Failure::Raise("Cannot mirroR empty shape");
|
||||
Standard_Failure::Raise(std::string(std::string(this->getFullLabel()) + ": Cannot mirror empty shape").c_str());
|
||||
gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z));
|
||||
gp_Trsf mat;
|
||||
mat.SetMirror(ax2);
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
App::PropertyLink Source;
|
||||
App::PropertyPosition Base;
|
||||
App::PropertyDirection Normal;
|
||||
App::PropertyLinkSub MirrorPlane;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
|
||||
@@ -29,10 +29,22 @@
|
||||
# define _USE_MATH_DEFINES
|
||||
# include <cmath>
|
||||
|
||||
# include <gp_Ax2.hxx>
|
||||
# include <gp_Circ.hxx>
|
||||
# include <gp_Dir.hxx>
|
||||
# include <gp_Pnt.hxx>
|
||||
# include <BRepAdaptor_Curve.hxx>
|
||||
# include <BRepAdaptor_Surface.hxx>
|
||||
# include <Geom_Plane.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <TopoDS_Face.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
|
||||
# include <cfloat>
|
||||
# include <QMessageBox>
|
||||
# include <QRegularExpression>
|
||||
# include <QTreeWidget>
|
||||
# include <QComboBox>
|
||||
#endif
|
||||
|
||||
#include <Base/Tools.h>
|
||||
@@ -50,13 +62,119 @@
|
||||
#include <Gui/ViewProvider.h>
|
||||
#include <Gui/WaitCursor.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Part/App/PrimitiveFeature.h>
|
||||
#include <Mod/Part/App/DatumFeature.h>
|
||||
#include <App/OriginFeature.h>
|
||||
|
||||
#include "Mirroring.h"
|
||||
|
||||
#include "ui_Mirroring.h"
|
||||
|
||||
|
||||
using namespace PartGui;
|
||||
|
||||
namespace PartGui {
|
||||
class MirrorPlaneSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
public:
|
||||
explicit MirrorPlaneSelection()
|
||||
: Gui::SelectionFilterGate()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* We can't simply check if the selection is a face or an edge because only certain faces
|
||||
* and edges can work. Bspline faces won't work, and only circle edges are supported. But we
|
||||
* also allow document object selections for part::plane, partdesign::plane, and origin planes,
|
||||
* as well as any part::feature with only a single face or a single circle edge. App::Links are
|
||||
* supported, provided the object they are linking to meets the above criteria.
|
||||
*/
|
||||
|
||||
bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override
|
||||
{
|
||||
std::string subString(sSubName);
|
||||
|
||||
if (pObj->isDerivedFrom(Part::Plane::getClassTypeId()) || pObj->isDerivedFrom<App::Plane>()
|
||||
|| (strstr(pObj->getNameInDocument(), "Plane") && pObj->isDerivedFrom(Part::Datum::getClassTypeId()))) {
|
||||
return true;
|
||||
// reference is an app::link or a part::feature or some subobject
|
||||
} else if (pObj->isDerivedFrom<Part::Feature>() || pObj->isDerivedFrom<App::Link>()) {
|
||||
bool isFace = false; //will be true if user selected face subobject or if object only has 1 face
|
||||
bool isEdge = false; //will be true if user selected edge subobject or if object only has 1 edge
|
||||
TopoDS_Shape shape;
|
||||
if (subString.length() > 0){
|
||||
shape = Part::Feature::getTopoShape(pObj, subString.c_str(), true).getShape();
|
||||
if (strstr(subString.c_str(), "Face")){
|
||||
isFace = true; //was face subobject, e.g. Face3
|
||||
} else {
|
||||
if (strstr(subString.c_str(), "Edge")){
|
||||
isEdge = true; //was edge subobject, e.g. Edge7
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shape = Part::Feature::getShape(pObj); //no subobjects were selected, so this is entire shape of feature
|
||||
}
|
||||
|
||||
// if there is only 1 face or 1 edge, then we don't need to force the user to select that face or edge
|
||||
// instead we can infer what was intended
|
||||
int faceCount = Part::TopoShape(shape).countSubShapes(TopAbs_FACE);
|
||||
int edgeCount = Part::TopoShape(shape).countSubShapes(TopAbs_EDGE);
|
||||
|
||||
TopoDS_Face face;
|
||||
TopoDS_Edge edge;
|
||||
|
||||
if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
|
||||
face = TopoDS::Face(shape);
|
||||
} else {
|
||||
if (faceCount == 1) { //entire feature selected, but it only has 1 face, so get that face
|
||||
TopoDS_Shape tdface = Part::TopoShape(shape).getSubShape(std::string("Face1").c_str());
|
||||
face = TopoDS::Face(tdface);
|
||||
isFace = true;
|
||||
}
|
||||
}
|
||||
if (!isFace && isEdge){ //don't bother with edge if we already have a face to work with
|
||||
edge = TopoDS::Edge(shape); //isEdge means an edge was selected
|
||||
} else {
|
||||
if (edgeCount == 1){ //we don't have a face yet and there were no edges in the subobject selection
|
||||
//but since this object only has 1 edge, we use it
|
||||
TopoDS_Shape tdedge = Part::TopoShape(shape).getSubShape(std::string("Edge1").c_str());
|
||||
edge = TopoDS::Edge(tdedge);
|
||||
isEdge = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFace && face.IsNull()) { //ensure we have a good face to work with
|
||||
return false;
|
||||
}
|
||||
if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with
|
||||
return false;
|
||||
}
|
||||
if (!isFace && !isEdge){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFace) {
|
||||
BRepAdaptor_Surface adapt(face);
|
||||
if (adapt.GetType() != GeomAbs_Plane){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (isEdge){
|
||||
BRepAdaptor_Curve curve(edge);
|
||||
if (!(curve.GetType() == GeomAbs_Circle)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} //end of if(derived from part::feature)
|
||||
return true;
|
||||
}//end of allow()
|
||||
|
||||
}; //end of class
|
||||
}; //end of namespace block
|
||||
|
||||
|
||||
/* TRANSLATOR PartGui::Mirroring */
|
||||
|
||||
Mirroring::Mirroring(QWidget* parent)
|
||||
@@ -75,6 +193,11 @@ Mirroring::Mirroring(QWidget* parent)
|
||||
sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
|
||||
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId()));
|
||||
sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId()));
|
||||
|
||||
connect(ui->selectButton, &QPushButton::clicked, this, &Mirroring::onSelectButtonClicked);
|
||||
|
||||
MirrorPlaneSelection* gate = new MirrorPlaneSelection();
|
||||
Gui::Selection().addSelectionGate(gate);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -82,6 +205,17 @@ Mirroring::Mirroring(QWidget* parent)
|
||||
*/
|
||||
Mirroring::~Mirroring() = default;
|
||||
|
||||
void Mirroring::onSelectButtonClicked(){
|
||||
if (!ui->selectButton->isChecked()){
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
ui->selectButton->setText(tr("Select reference"));
|
||||
} else {
|
||||
MirrorPlaneSelection* gate = new MirrorPlaneSelection();
|
||||
Gui::Selection().addSelectionGate(gate);
|
||||
ui->selectButton->setText(tr("Selecting"));
|
||||
}
|
||||
}
|
||||
|
||||
void Mirroring::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
@@ -90,6 +224,20 @@ void Mirroring::changeEvent(QEvent *e)
|
||||
QWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
void Mirroring::onSelectionChanged(const Gui::SelectionChanges &msg)
|
||||
{
|
||||
if (ui->selectButton->isChecked()) {
|
||||
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
||||
std::string objName(msg.pObjectName);
|
||||
std::string subName(msg.pSubName);
|
||||
std::stringstream refStr;
|
||||
refStr << objName << " : [" << subName << "]";
|
||||
ui->referenceLineEdit->setText(QLatin1String(refStr.str().c_str()));
|
||||
ui->comboBox->setCurrentIndex(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mirroring::findShapes()
|
||||
{
|
||||
App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
||||
@@ -119,6 +267,12 @@ void Mirroring::findShapes()
|
||||
}
|
||||
}
|
||||
|
||||
bool Mirroring::reject()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mirroring::accept()
|
||||
{
|
||||
if (ui->shapes->selectedItems().isEmpty()) {
|
||||
@@ -138,17 +292,26 @@ bool Mirroring::accept()
|
||||
unsigned int count = activeDoc->countObjectsOfType(Base::Type::fromName("Part::Mirroring"));
|
||||
activeDoc->openTransaction("Mirroring");
|
||||
|
||||
QString shape, label;
|
||||
QString shape, label, selectionString;
|
||||
QRegularExpression rx(QString::fromLatin1(R"( \(Mirror #\d+\)$)"));
|
||||
QList<QTreeWidgetItem *> items = ui->shapes->selectedItems();
|
||||
float normx=0, normy=0, normz=0;
|
||||
int index = ui->comboBox->currentIndex();
|
||||
if (index == 0)
|
||||
std::string selection(""); //set MirrorPlane property to empty string unless
|
||||
//user has selected Use selected reference in combobox
|
||||
|
||||
if (index == 0){
|
||||
normz = 1.0f;
|
||||
else if (index == 1)
|
||||
} else if (index == 1){
|
||||
normy = 1.0f;
|
||||
else
|
||||
} else if (index == 2){
|
||||
normx = 1.0f;
|
||||
} else if (index == 3){ //use selected reference
|
||||
std::vector<Gui::SelectionObject> selobjs = Gui::Selection().getSelectionEx();
|
||||
if (selobjs.size() == 1) {
|
||||
selection = selobjs[0].getAsPropertyLinkSubString();
|
||||
}
|
||||
}
|
||||
double basex = ui->baseX->value().getValue();
|
||||
double basey = ui->baseY->value().getValue();
|
||||
double basez = ui->baseZ->value().getValue();
|
||||
@@ -156,6 +319,7 @@ bool Mirroring::accept()
|
||||
shape = item->data(0, Qt::UserRole).toString();
|
||||
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(item->text(0).toUtf8());
|
||||
label = QString::fromStdString(escapedstr);
|
||||
selectionString = QString::fromStdString(selection);
|
||||
|
||||
// if we already have the suffix " (Mirror #<number>)" remove it
|
||||
int pos = label.indexOf(rx);
|
||||
@@ -170,10 +334,12 @@ bool Mirroring::accept()
|
||||
"__doc__.ActiveObject.Label=u\"%3\"\n"
|
||||
"__doc__.ActiveObject.Normal=(%4,%5,%6)\n"
|
||||
"__doc__.ActiveObject.Base=(%7,%8,%9)\n"
|
||||
"__doc__.ActiveObject.MirrorPlane=(%10)\n"
|
||||
"del __doc__")
|
||||
.arg(this->document, shape, label)
|
||||
.arg(normx).arg(normy).arg(normz)
|
||||
.arg(basex).arg(basey).arg(basez);
|
||||
.arg(basex).arg(basey).arg(basez)
|
||||
.arg(selectionString);
|
||||
Gui::Command::runCommand(Gui::Command::App, code.toLatin1());
|
||||
QByteArray from = shape.toLatin1();
|
||||
Gui::Command::copyVisual("ActiveObject", "ShapeColor", from);
|
||||
@@ -183,6 +349,7 @@ bool Mirroring::accept()
|
||||
|
||||
activeDoc->commitTransaction();
|
||||
activeDoc->recompute();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class Property;
|
||||
namespace PartGui {
|
||||
|
||||
class Ui_Mirroring;
|
||||
class Mirroring : public QWidget
|
||||
class Mirroring : public QWidget, public Gui::SelectionObserver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -43,14 +43,17 @@ public:
|
||||
explicit Mirroring(QWidget* parent = nullptr);
|
||||
~Mirroring() override;
|
||||
bool accept();
|
||||
bool reject();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void findShapes();
|
||||
void onSelectButtonClicked();
|
||||
|
||||
private:
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
QString document;
|
||||
std::unique_ptr<Ui_Mirroring> ui;
|
||||
};
|
||||
|
||||
@@ -14,54 +14,7 @@
|
||||
<string>Mirroring</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QTreeWidget" name="shapes">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Shapes</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Mirror plane:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XY plane</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XZ plane</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>YZ plane</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Base point</string>
|
||||
@@ -139,6 +92,81 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Mirror plane:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XY plane</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XZ plane</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>YZ plane</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use selected reference</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QTreeWidget" name="shapes">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Shapes</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="selectButton">
|
||||
<property name="text">
|
||||
<string>Selecting</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="referenceLineEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Mirror plane reference</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
||||
@@ -74,9 +74,18 @@ ViewProviderMirror::~ViewProviderMirror()
|
||||
|
||||
void ViewProviderMirror::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
|
||||
{
|
||||
// don't add plane editor to context menu if MirrorPlane is set because it would override any changes, anyway
|
||||
Part::Mirroring* mf = static_cast<Part::Mirroring*>(getObject());
|
||||
Part::Feature* ref = static_cast<Part::Feature*>(mf->MirrorPlane.getValue());
|
||||
bool enabled = true;
|
||||
if (ref){
|
||||
enabled = false;
|
||||
}
|
||||
QAction* act;
|
||||
act = menu->addAction(QObject::tr("Edit mirror plane"), receiver, member);
|
||||
act->setEnabled(enabled);
|
||||
act->setData(QVariant((int)ViewProvider::Default));
|
||||
|
||||
ViewProviderPart::setupContextMenu(menu, receiver, member);
|
||||
}
|
||||
|
||||
@@ -85,6 +94,10 @@ bool ViewProviderMirror::setEdit(int ModNum)
|
||||
if (ModNum == ViewProvider::Default) {
|
||||
// get the properties from the mirror feature
|
||||
Part::Mirroring* mf = static_cast<Part::Mirroring*>(getObject());
|
||||
Part::Feature* ref = static_cast<Part::Feature*>(mf->MirrorPlane.getValue());
|
||||
if (ref) { //skip this editor if MirrorPlane property is set
|
||||
return false;
|
||||
}
|
||||
Base::BoundBox3d bbox = mf->Shape.getBoundingBox();
|
||||
float len = (float)bbox.CalcDiagonalLength();
|
||||
Base::Vector3d base = mf->Base.getValue();
|
||||
|
||||
Reference in New Issue
Block a user