Commit af6b54bd authored by David Thompson's avatar David Thompson

WIP: Finalize.

parent b6f2021a
......@@ -11,6 +11,7 @@
#include "smtk/extension/paraview/widgets/ui_pqConePropertyWidget.h"
#include "pqCoreUtilities.h"
#include "pqPointPickingHelper.h"
#include "vtkSMNewWidgetRepresentationProxy.h"
#include "vtkSMProperty.h"
......@@ -19,11 +20,21 @@
#include "vtkCommand.h"
#include "vtkMath.h"
#include "vtkVector.h"
#include "vtkVectorOperators.h"
class pqConePropertyWidget::Internals
{
public:
Internals()
: PickPoint1(true)
, BottomRadiusName("Bottom radius")
{
}
Ui::ConePropertyWidget Ui;
std::string BottomRadiusName;
bool PickPoint1;
};
pqConePropertyWidget::pqConePropertyWidget(
......@@ -34,23 +45,33 @@ pqConePropertyWidget::pqConePropertyWidget(
Ui::ConePropertyWidget& ui = m_p->Ui;
ui.setupUi(this);
auto cyl = smgroup->GetProperty("Cylindrical");
if (!cyl)
auto topRad = smgroup->GetProperty("TopRadius");
if (!topRad)
{
// Only show a single radius when both must be identical.
ui.radius2->hide();
ui.labelRadius2->hide();
ui.labelRadius1->setText("Radius");
ui.show3DWidget->setText("Show cylinder");
ui.cylindrical->hide(); // We are forced into cylinder model
auto cyl = smgroup->GetProperty("Cylindrical");
if (cyl)
{
int on = 1;
vtkSMPropertyHelper(cyl).Set(&on, 1);
}
}
this->connect(ui.xAxis, SIGNAL(clicked()), SLOT(useXAxis()));
this->connect(ui.yAxis, SIGNAL(clicked()), SLOT(useYAxis()));
this->connect(ui.zAxis, SIGNAL(clicked()), SLOT(useZAxis()));
// link show3DWidget checkbox
this->connect(ui.show3DWidget, SIGNAL(toggled(bool)), SLOT(setWidgetVisible(bool)));
ui.show3DWidget->connect(this, SIGNAL(widgetVisibilityToggled(bool)), SLOT(setChecked(bool)));
this->setWidgetVisible(ui.show3DWidget->isChecked());
// link show3DWidget checkbox
this->connect(ui.cylindrical, SIGNAL(toggled(bool)), SLOT(setCylindrical(bool)));
// ui.cylindrical->connect(this, SIGNAL(widgetVisibilityToggled(bool)), SLOT(setChecked(bool)));
this->setCylindrical(ui.cylindrical->isChecked());
#ifdef Q_OS_MAC
ui.pickLabel->setText(ui.pickLabel->text().replace("Ctrl", "Cmd"));
#endif
......@@ -108,27 +129,138 @@ pqConePropertyWidget::pqConePropertyWidget(
{
qCritical("Missing required property for function 'TopRadius'.");
}
pqPointPickingHelper* pickHelper = new pqPointPickingHelper(QKeySequence(tr("P")), false, this);
pickHelper->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper, SIGNAL(pick(double, double, double)), SLOT(pick(double, double, double)));
pqPointPickingHelper* pickHelper2 =
new pqPointPickingHelper(QKeySequence(tr("Ctrl+P")), true, this);
pickHelper2->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper2->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper2, SIGNAL(pick(double, double, double)), SLOT(pick(double, double, double)));
pqPointPickingHelper* pickHelper3 = new pqPointPickingHelper(QKeySequence(tr("1")), false, this);
pickHelper3->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper3->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper3, SIGNAL(pick(double, double, double)), SLOT(pickPoint1(double, double, double)));
pqPointPickingHelper* pickHelper4 =
new pqPointPickingHelper(QKeySequence(tr("Ctrl+1")), true, this);
pickHelper4->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper4->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper4, SIGNAL(pick(double, double, double)), SLOT(pickPoint1(double, double, double)));
pqPointPickingHelper* pickHelper5 = new pqPointPickingHelper(QKeySequence(tr("2")), false, this);
pickHelper5->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper5->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper5, SIGNAL(pick(double, double, double)), SLOT(pickPoint2(double, double, double)));
pqPointPickingHelper* pickHelper6 =
new pqPointPickingHelper(QKeySequence(tr("Ctrl+2")), true, this);
pickHelper6->connect(this, SIGNAL(viewChanged(pqView*)), SLOT(setView(pqView*)));
pickHelper6->connect(this, SIGNAL(widgetVisibilityUpdated(bool)), SLOT(setShortcutEnabled(bool)));
this->connect(
pickHelper6, SIGNAL(pick(double, double, double)), SLOT(pickPoint2(double, double, double)));
pqCoreUtilities::connect(
this->widgetProxy(), vtkCommand::PropertyModifiedEvent, this, SLOT(updateInformationLabels()));
this->updateInformationLabels();
}
pqConePropertyWidget::~pqConePropertyWidget()
{
}
void pqConePropertyWidget::useXAxis()
void pqConePropertyWidget::pick(double wx, double wy, double wz)
{
if (m_p->PickPoint1)
{
this->pickPoint1(wx, wy, wz);
}
else
{
this->pickPoint2(wx, wy, wz);
}
m_p->PickPoint1 = !m_p->PickPoint1;
}
void pqConePropertyWidget::useYAxis()
void pqConePropertyWidget::pickPoint1(double wx, double wy, double wz)
{
double position[3] = { wx, wy, wz };
vtkSMNewWidgetRepresentationProxy* wdgProxy = this->widgetProxy();
vtkSMPropertyHelper(wdgProxy, "BottomPoint").Set(position, 3);
wdgProxy->UpdateVTKObjects();
emit this->changeAvailable();
this->render();
}
void pqConePropertyWidget::useZAxis()
void pqConePropertyWidget::pickPoint2(double wx, double wy, double wz)
{
double position[3] = { wx, wy, wz };
vtkSMNewWidgetRepresentationProxy* wdgProxy = this->widgetProxy();
vtkSMPropertyHelper(wdgProxy, "TopPoint").Set(position, 3);
wdgProxy->UpdateVTKObjects();
emit this->changeAvailable();
this->render();
}
void pqConePropertyWidget::setCylindrical(bool isCylinder)
{
int cyl = isCylinder ? 1 : 0;
vtkSMNewWidgetRepresentationProxy* wdgProxy = this->widgetProxy();
vtkSMPropertyHelper(wdgProxy, "Cylindrical").Set(&cyl, 1);
if (isCylinder)
{
m_p->BottomRadiusName = m_p->Ui.labelRadius1->text().toStdString();
m_p->Ui.radius2->hide();
m_p->Ui.labelRadius2->hide();
m_p->Ui.labelRadius1->setText("Radius");
m_p->Ui.show3DWidget->setText("Show cylinder");
}
else
{
m_p->Ui.radius2->show();
m_p->Ui.labelRadius2->show();
m_p->Ui.labelRadius1->setText(m_p->BottomRadiusName.c_str());
m_p->Ui.show3DWidget->setText("Show cone");
}
wdgProxy->UpdateVTKObjects();
emit this->changeAvailable();
this->render();
}
void pqConePropertyWidget::setForceCylindrical(bool isCylinder)
{
m_p->Ui.cylindrical->setChecked(isCylinder);
if (isCylinder)
{
m_p->Ui.cylindrical->hide();
}
else
{
m_p->Ui.cylindrical->show();
}
this->setCylindrical(isCylinder);
}
void pqConePropertyWidget::updateInformationLabels()
{
Ui::ConePropertyWidget& ui = m_p->Ui;
vtkVector3d p1, p2;
vtkSMProxy* wproxy = this->widgetProxy();
vtkSMPropertyHelper(wproxy, "BottomPoint").Get(p1.GetData(), 3);
vtkSMPropertyHelper(wproxy, "TopPoint").Get(p2.GetData(), 3);
double distance = (p2 - p1).Norm();
ui.labelLength->setText(QString("<b>Length:</b> <i>%1</i> ").arg(distance));
}
void pqConePropertyWidget::placeWidget()
......
......@@ -22,9 +22,14 @@ public:
~pqConePropertyWidget() override;
public slots:
void useXAxis();
void useYAxis();
void useZAxis();
void pick(double, double, double);
void pickPoint1(double, double, double);
void pickPoint2(double, double, double);
/// Force the widget to accept a single, positive radius (when true).
void setCylindrical(bool);
/// The same as setCylindrical, but also hide the checkbox that
/// allows user control of whether the cone is a cylinder (when true).
void setForceCylindrical(bool);
protected slots:
void updateInformationLabels();
......
......@@ -40,6 +40,7 @@ using AttributeItemInfo = smtk::extension::AttributeItemInfo;
pqSMTKConeItemWidget::pqSMTKConeItemWidget(
const smtk::extension::AttributeItemInfo& info, Qt::Orientation orient)
: pqSMTKAttributeItemWidget(info, orient)
, m_forceCylinder(false)
{
this->createWidget();
}
......@@ -53,6 +54,13 @@ qtItem* pqSMTKConeItemWidget::createConeItemWidget(const AttributeItemInfo& info
return new pqSMTKConeItemWidget(info);
}
qtItem* pqSMTKConeItemWidget::createCylinderItemWidget(const AttributeItemInfo& info)
{
auto item = new pqSMTKConeItemWidget(info);
item->setForceCylindrical(true);
return item;
}
bool pqSMTKConeItemWidget::createProxyAndWidget(
vtkSMProxy*& proxy, pqInteractivePropertyWidget*& widget)
{
......@@ -75,7 +83,9 @@ bool pqSMTKConeItemWidget::createProxyAndWidget(
{
return false;
}
widget = new pqConePropertyWidget(proxy, proxy->GetPropertyGroup(0));
auto coneWidget = new pqConePropertyWidget(proxy, proxy->GetPropertyGroup(0));
coneWidget->setForceCylindrical(m_forceCylinder);
widget = coneWidget;
// II. Initialize the properties.
m_p->m_pvwidget = widget;
......@@ -143,6 +153,7 @@ void pqSMTKConeItemWidget::updateItemFromWidget()
items[1]->setValues(pt1.GetData(), pt1.GetData() + 3);
items[2]->setValue(rd0);
}
break;
case ItemBindings::ConePointsRadii:
if (curPt0 != pt0 || curPt1 != pt1 || curRd0 != rd0 || curRd1 != rd1)
{
......@@ -195,7 +206,7 @@ void pqSMTKConeItemWidget::updateWidgetFromItem()
}
break;
case ItemBindings::CylinderPointsRadius:
// We could set TopRadius here, but we shouldn't need to.
vtkSMPropertyHelper(widget, "TopRadius").Set(&radius, 1);
break;
case ItemBindings::Invalid:
default:
......@@ -206,6 +217,22 @@ void pqSMTKConeItemWidget::updateWidgetFromItem()
}
}
bool pqSMTKConeItemWidget::setForceCylindrical(bool isCylinder)
{
if (m_forceCylinder == isCylinder)
{
return false;
}
m_forceCylinder = isCylinder;
if (m_p->m_pvwidget)
{
auto widget = reinterpret_cast<pqConePropertyWidget*>(m_p->m_pvwidget);
widget->setForceCylindrical(m_forceCylinder);
}
return true;
}
bool pqSMTKConeItemWidget::fetchConeItems(
ItemBindings& binding, std::vector<smtk::attribute::DoubleItemPtr>& items)
{
......@@ -242,6 +269,11 @@ bool pqSMTKConeItemWidget::fetchConeItems(
{
rd1ItemName = "TopRadius";
}
// For cylinders, use the same attribute item for both radii.
if (m_itemInfo.component().attribute("Radius", rd0ItemName))
{
rd1ItemName = rd0ItemName;
}
auto pt0Item = groupItem->findAs<smtk::attribute::DoubleItem>(pt0ItemName);
auto pt1Item = groupItem->findAs<smtk::attribute::DoubleItem>(pt1ItemName);
auto rd0Item = groupItem->findAs<smtk::attribute::DoubleItem>(rd0ItemName);
......@@ -255,7 +287,7 @@ bool pqSMTKConeItemWidget::fetchConeItems(
items.push_back(pt0Item);
items.push_back(pt1Item);
items.push_back(rd0Item);
if (!rd1Item)
if (!rd1Item || rd1Item == rd0Item)
{
binding = ItemBindings::CylinderPointsRadius;
return true;
......
......@@ -20,8 +20,8 @@
* Each endpoint may have an associated radius, one of which may
* be zero.
*
* If only one radius is provided, then the cone is forced to be a
* cylinder with the given radius.
* If setForceCylindrical(true) is called, then the cone is forced to be a
* cylinder with a single radius value accepted.
*
* Note that the widget does not allow an interior apex (i.e., neither
* of the the radii may be negative).
......@@ -30,7 +30,11 @@
* children specifying a center, Euler angles, and a length)
* may be supported.
*
* Currently, there is no support to initialize the box coordinates.
* Currently, there is no support to initialize the placement to
* a given set of bounds; if you use this widget as part of the
* user interface to an operation, implement a configure() method
* on the operation to contextually place the widget based on
* associations.
*/
class SMTKPQWIDGETSEXT_EXPORT pqSMTKConeItemWidget : public pqSMTKAttributeItemWidget
{
......@@ -40,13 +44,25 @@ public:
const smtk::extension::AttributeItemInfo& info, Qt::Orientation orient = Qt::Horizontal);
virtual ~pqSMTKConeItemWidget();
/// Create an instance of the widget that allows users to define a cone.
static qtItem* createConeItemWidget(const AttributeItemInfo& info);
/// Create an instance of the widget that allows users to define a cylinder.
static qtItem* createCylinderItemWidget(const AttributeItemInfo& info);
bool createProxyAndWidget(vtkSMProxy*& proxy, pqInteractivePropertyWidget*& widget) override;
/// Retrieve property values from ParaView proxy and store them in the attribute's Item.
void updateItemFromWidget() override;
/// Retrieve property values from the attribute's Item and update the ParaView proxy.
void updateWidgetFromItem() override;
public slots:
/**\brief Change the user interface so that only cylinders are accepted (when passed true).
*
* This method returns true when the user interface changes state
* as a result of the call and false otherwise.
*/
bool setForceCylindrical(bool);
protected:
/// Describe how an attribute's items specify a cone or cylinder.
enum class ItemBindings
......@@ -70,6 +86,8 @@ protected:
* this method returns false.
*/
bool fetchConeItems(ItemBindings& binding, std::vector<smtk::attribute::DoubleItemPtr>& items);
bool m_forceCylinder;
};
#endif // smtk_extension_paraview_widgets_pqSMTKConeItemWidget_h
......@@ -47,6 +47,8 @@ void pqSMTKWidgetsAutoStart::startup()
// Register qtItem widget subclasses implemented using ParaView 3-D widgets:
qtSMTKUtilities::registerItemConstructor("Box", pqSMTKBoxItemWidget::createBoxItemWidget);
qtSMTKUtilities::registerItemConstructor("Cone", pqSMTKConeItemWidget::createConeItemWidget);
qtSMTKUtilities::registerItemConstructor(
"Cylinder", pqSMTKConeItemWidget::createCylinderItemWidget);
qtSMTKUtilities::registerItemConstructor(
"InfiniteCylinder", pqSMTKInfiniteCylinderItemWidget::createCylinderItemWidget);
qtSMTKUtilities::registerItemConstructor("Line", pqSMTKLineItemWidget::createLineItemWidget);
......
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>379</width>
<height>255</height>
<height>187</height>
</rect>
</property>
<property name="windowTitle">
......@@ -29,7 +29,30 @@
<property name="spacing">
<number>2</number>
</property>
<item row="3" column="0">
<item row="8" column="0">
<widget class="QLabel" name="labelRadius2">
<property name="text">
<string>Radius 2</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="labelRadius1">
<property name="text">
<string>Radius 1</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="pqDoubleLineEdit" name="radius2"/>
</item>
<item row="7" column="1">
<widget class="pqDoubleLineEdit" name="radius1"/>
</item>
<item row="5" column="1">
<widget class="pqDoubleLineEdit" name="point1X"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelPoint1">
<property name="text">
<string>Point 1</string>
......@@ -39,16 +62,13 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="pqDoubleLineEdit" name="point1X"/>
</item>
<item row="3" column="2">
<item row="5" column="2">
<widget class="pqDoubleLineEdit" name="point1Y"/>
</item>
<item row="3" column="3">
<item row="5" column="3">
<widget class="pqDoubleLineEdit" name="point1Z"/>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QLabel" name="labelPoint2">
<property name="text">
<string>Point 2</string>
......@@ -58,16 +78,16 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<widget class="pqDoubleLineEdit" name="point2X"/>
</item>
<item row="4" column="2">
<item row="6" column="2">
<widget class="pqDoubleLineEdit" name="point2Y"/>
</item>
<item row="4" column="3">
<item row="6" column="3">
<widget class="pqDoubleLineEdit" name="point2Z"/>
</item>
<item row="7" column="0" colspan="4">
<item row="10" column="0" colspan="4">
<widget class="QLabel" name="pickLabel">
<property name="font">
<font>
......@@ -86,41 +106,6 @@
</property>
</widget>
</item>
<item row="8" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QPushButton" name="xAxis">
<property name="text">
<string>X Axis</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="yAxis">
<property name="text">
<string>Y Axis</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="zAxis">
<property name="text">
<string>Z Axis</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0" colspan="4">
<widget class="QPushButton" name="centerOnBounds">
<property name="text">
<string>Center on Bounds</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="show3DWidget">
<property name="text">
......@@ -131,33 +116,20 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<item row="9" column="0">
<widget class="QLabel" name="labelLength">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Length: &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;na&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="pqDoubleLineEdit" name="radius1"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelRadius1">
<property name="text">
<string>Radius 1</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="labelRadius2">
<item row="4" column="0">
<widget class="QCheckBox" name="cylindrical">
<property name="text">
<string>Radius 2</string>
<string>Cylinder</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="pqDoubleLineEdit" name="radius2"/>
</item>
</layout>
</widget>
<customwidgets>
......@@ -167,6 +139,17 @@
<header>pqDoubleLineEdit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>show3DWidget</tabstop>
<tabstop>point1X</tabstop>
<tabstop>point1Y</tabstop>
<tabstop>point1Z</tabstop>
<tabstop>point2X</tabstop>
<tabstop>point2Y</tabstop>
<tabstop>point2Z</tabstop>
<tabstop>radius1</tabstop>
<tabstop>radius2</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
......@@ -166,20 +166,44 @@ bool vtkConeRepresentation::SetEndpoint(bool isBottom, const vtkVector3d& pt)
{
vtkVector3d p0(this->Cone->GetBottomPoint());
vtkVector3d p1(this->Cone->GetTopPoint());
// We cannot allow coincident endpoints,
// and if pt is already the existing value, do nothing.
if (p0 == pt || p1 == pt)
{
return false;
}
vtkVector3d temp(pt); // Because vtkSetVectorMacro is not const correct.
if (isBottom)
{
if (p0 == pt)
{
// If pt is already the existing value, do nothing.
return false;
}
else if (p1 == pt)
{
// We cannot allow coincident endpoints, but we can
// swap endpoints (and radii), which allows the
// ParaView widget to initialize the representation
// even when the new bottom/top point is coincident
// with the old top/bottom point.
this->SwapTopAndBottom();
return true;
}
this->Cone->SetBottomPoint(temp.GetData());
}
else
{
if (p1 == pt)
{
// If pt is already the existing value, do nothing.
return false;
}
else if (p0 == pt)
{
// We cannot allow coincident endpoints, but we can
// swap endpoints (and radii), which allows the
// ParaView widget to initialize the representation
// even when the new bottom/top point is coincident
// with the old top/bottom point.
this->SwapTopAndBottom();
return true;
}
this->Cone->SetTopPoint(temp.GetData());
}
this->Modified();
......@@ -208,7 +232,12 @@ bool vtkConeRepresentation::SetRadius(bool isBottom, double r)
{
return false;
}
if (isBottom)
if (this->Cylindrical)
{
this->Cone->SetBottomRadius(r);
this->Cone->SetTopRadius(r);
}
else if (isBottom)
{
this->Cone->SetBottomRadius(r);
}
......@@ -1409,3 +1438,16 @@ void vtkConeRepresentation::RegisterPickers()
}
pm->AddPicker(this->Picker, this);
}
void vtkConeRepresentation::SwapTopAndBottom()
{
vtkVector3d p0(this->Cone->GetBottomPoint());
vtkVector3d p1(this->Cone->GetTopPoint());
double r0 = this->Cone->GetBottomRadius();
double r1 = this->Cone->GetTopRadius();
this->Cone->SetTopPoint(p0.GetData());
this->Cone->SetBottomPoint(p1.GetData());
this->Cone->SetTopRadius(r0);
this->Cone->SetBottomRadius(r1);
this->Modified();
}
This diff is collapsed.
This diff is collapsed.
......@@ -340,6 +340,9 @@ public:
*/
void RegisterPickers() override;
/// Swap the top and bottom points and radii.
void SwapTopAndBottom();
protected:
vtkConeRepresentation();
~vtkConeRepresentation() override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment