Refer to Measurements Example
MeasurementsApplication.hxx
#ifndef __MeasurementsApplication_HeaderFile
#define __MeasurementsApplication_HeaderFile
#include "../baseviewer/BaseViewerApplication.hxx"
#include <cadex/Base_AngleUnit.hxx>
#include <cadex/Base_LengthUnit.hxx>
#include <cadex/ModelData_Shape.hxx>
#include <cadex/ModelPrs_Measurement.hxx>
#include <cadex/ModelPrs_MeasurementType.hxx>
#include <cadex/ModelPrs_SelectionChangesObserver.hxx>
class ModelPrs_Selection;
class ModelPrs_SceneNode;
}
class MeasurementsApplication;
{
public:
SelectionChangesObserver (MeasurementsApplication& theApplication);
void Clear();
private:
MeasurementsApplication& myApplication;
std::vector<cadex::ModelData_Shape> myShapes;
};
class MeasurementInfo : public QObject
{
Q_OBJECT
Q_PROPERTY (int numberOfSelectedShapes MEMBER myNumberOfSelectedShapes WRITE setNumberOfSelectedShapes NOTIFY numberOfSelectedShapesChanged)
Q_PROPERTY (QString measurementsResults MEMBER myMeasurementsResults WRITE setMeasurementsResults NOTIFY measurementsResultsChanged)
public:
MeasurementInfo (MeasurementsApplication* theParent);
void setNumberOfSelectedShapes (int theValue);
void setMeasurementsResults (const QString& theValue);
signals:
void numberOfSelectedShapesChanged();
void measurementsResultsChanged();
private:
int myNumberOfSelectedShapes = 0;
QString myMeasurementsResults;
};
class MeasurementsApplication : public BaseViewerApplication
{
Q_OBJECT
public:
MeasurementsApplication();
void OnSelectionChanged (const std::vector<cadex::ModelData_Shape>& theShapes);
public slots:
void onInitialized();
void onMeasurementModeChanged (const QVariant& theMode);
void onMeasurementAngleUnitsChanged (const QVariant& theIndex);
void onMeasurementLengthUnitsChanged (const QVariant& theIndex);
protected:
void Clear() override;
void UpdateResult();
protected:
SelectionChangesObserver mySelectionObserver;
double myMeasurementSize;
cadex::Base_AngleUnit myAngleUnit;
std::vector<cadex::ModelPrs_Measurement> myMeasurements;
MeasurementInfo* myMeasurementInfo;
};
#endif // __MeasurementsApplication_HeaderFile
MeasurementsApplication.cxx
#include "MeasurementsApplication.hxx"
#include <cadex/ModelAlgo_BoundingBox.hxx>
#include <cadex/ModelAlgo_TransformationApplier.hxx>
#include <cadex/ModelData_Appearance.hxx>
#include <cadex/ModelData_Box.hxx>
#include <cadex/ModelData_Color.hxx>
#include <cadex/ModelData_Face.hxx>
#include <cadex/ModelData_Transformation.hxx>
#include <cadex/ModelData_Vertex.hxx>
#include <cadex/ModelPrsQtQuick_ViewPort.hxx>
#include <cadex/ModelPrs_Geometry.hxx>
#include <cadex/ModelPrs_Measurement.hxx>
#include <cadex/ModelPrs_MeasurementFactory.hxx>
#include <cadex/ModelPrs_SceneNode.hxx>
#include <cadex/ModelPrs_SceneNodeFactory.hxx>
#include <cadex/ModelPrs_Selection.hxx>
#include <cadex/ModelPrs_SelectionManager.hxx>
#include <cadex/ModelPrs_SelectionVisitor.hxx>
#include <QtCore/QDebug>
#include <QtQuick/QQuickWindow>
#include <deque>
#include <memory>
namespace {
{
public:
{
}
void Visit (
const ModelPrs_SceneNode& theNode,
const std::vector<ModelData_Shape>& theShapes)
override
{
auto aParent = theNode.
Parent();
while (aParent) {
aParent = aParent.Parent();
}
myShape = theShapes.front();
}
};
{
switch (theType) {
return 1;
return 2;
return 3;
default:
return 0;
}
}
{
switch (theType) {
default:
}
}
{
switch (theType) {
default:
}
}
std::vector<ModelPrs_Measurement> CreateMeasurements (const std::vector<cadex::ModelData_Shape>& theShapes,
{
std::vector<ModelPrs_Measurement> aMeasurements;
switch (theType) {
if (aMinorRadius) {
aMeasurements.push_back (aMinorRadius);
}
if (aMajorRadius && aMinorRadius != aMajorRadius) {
aMeasurements.push_back (aMajorRadius);
}
break;
}
if (aMinorDiameter) {
aMeasurements.push_back (aMinorDiameter);
}
if (aMajorDiameter && aMinorDiameter != aMajorDiameter) {
aMeasurements.push_back (aMajorDiameter);
}
break;
}
auto aDistance = aFactory.
CreateDistance (theShapes[0], theShapes[1]);
if (aDistance) {
aMeasurements.push_back (aDistance);
}
break;
}
Q_ASSERT (theShapes[0].Type() == ModelData_ST_Face && theShapes[1].Type() == ModelData_ST_Face);
auto anAngle = aFactory.
CreateAngle (aFirstFace, aSecondFace);
if (anAngle) {
aMeasurements.push_back (anAngle);
}
break;
}
Q_ASSERT (theShapes[0].Type() == ModelData_ST_Vertex
&& theShapes[1].Type() == ModelData_ST_Vertex
&& theShapes[2].Type() == ModelData_ST_Vertex);
const auto& aFirstVertex = ModelData_Vertex::Cast (theShapes[0]);
const auto& aSecondVertex = ModelData_Vertex::Cast (theShapes[1]);
const auto& aThirdVertex = ModelData_Vertex::Cast (theShapes[2]);
auto anAngle = aFactory.
CreateAngle (aFirstVertex, aSecondVertex, aThirdVertex);
if (anAngle) {
aMeasurements.push_back (anAngle);
}
break;
}
default:
Q_ASSERT(false);
}
return aMeasurements;
}
}
SelectionChangesObserver::SelectionChangesObserver (MeasurementsApplication& theApplication)
: myApplication (theApplication)
{
}
{
Clear();
} else {
SelectionVisitor aVisitor;
myShapes.push_back (aVisitor.myShape);
}
myApplication.OnSelectionChanged (myShapes);
}
void SelectionChangesObserver::Clear()
{
myShapes.clear();
}
MeasurementInfo::MeasurementInfo (MeasurementsApplication* theParent)
: QObject (theParent)
{
}
void MeasurementInfo::setNumberOfSelectedShapes (int theValue)
{
if (myNumberOfSelectedShapes != theValue) {
myNumberOfSelectedShapes = theValue;
emit numberOfSelectedShapesChanged();
}
}
void MeasurementInfo::setMeasurementsResults (const QString& theValue)
{
if (myMeasurementsResults != theValue) {
myMeasurementsResults = theValue;
emit measurementsResultsChanged();
}
}
MeasurementsApplication::MeasurementsApplication()
: mySelectionObserver (*this),
myMeasurementSize (-1.),
myAngleUnit (Base_AU_Degree),
myLengthUnit (Base_LU_Millimeters)
{
myMeasurementInfo = new MeasurementInfo (this);
myScene.SelectionManager().Register (mySelectionObserver);
connect (this, &BaseViewerApplication::initialized, this, &MeasurementsApplication::onInitialized);
}
void MeasurementsApplication::onMeasurementModeChanged (const QVariant& theMode)
{
myMeasurementMode = static_cast<ModelPrs_MeasurementType> (theMode.toInt());
auto aSelectionMode = GetSelectionModeForMeasurement (myMeasurementMode);
myRoot.SetSelectionMode (static_cast<ModelPrs_SelectionMode> (aSelectionMode));
auto aSelectionFilter = GetSelectionFilterForMeasurement (myMeasurementMode);
myScene.SelectionManager().SetFilter (static_cast<ModelPrs_SelectionFilterType> (aSelectionFilter));
mySelectionObserver.Clear();
myRoot.RemoveChildNode (myRootMeasurementsNode);
myScene.Update();
}
void MeasurementsApplication::onMeasurementAngleUnitsChanged (const QVariant& theIndex)
{
myAngleUnit = static_cast<Base_AngleUnit> (theIndex.toInt());
UpdateResult();
myRootMeasurementsNode.Invalidate();
myScene.Update();
}
void MeasurementsApplication::onMeasurementLengthUnitsChanged (const QVariant& theIndex)
{
myLengthUnit = static_cast<Base_LengthUnit> (theIndex.toInt());
UpdateResult();
myRootMeasurementsNode.Invalidate();
myScene.Update();
}
namespace {
{
ModelAlgo_BoundingBox::Compute (theModel, aModelBoundingBox);
auto r = std::max ({ aModelBoundingBox.
XRange(),
aModelBoundingBox.
ZRange() });
return r / 16.;
} else {
return 10.;
}
}
}
void MeasurementsApplication::UpdateResult()
{
QStringList aResult;
for (auto& aMeasurement : myMeasurements) {
aMeasurement.SetAngleUnit (myAngleUnit);
aMeasurement.SetLengthUnit (myLengthUnit);
aResult.push_back (QString::number (aMeasurement.Value(), 'g', 3));
}
myMeasurementInfo->setMeasurementsResults (aResult.join ("; "));
}
void MeasurementsApplication::OnSelectionChanged (const std::vector<cadex::ModelData_Shape>& theShapes)
{
myMeasurementInfo->setNumberOfSelectedShapes (static_cast<int> (theShapes.size()));
if (theShapes.empty() || theShapes.size() < NumberOfRequiredShapes (myMeasurementMode)) {
return;
}
myMeasurements = CreateMeasurements (theShapes, myMeasurementMode);
UpdateResult();
if (myMeasurements.empty()) {
myMeasurementInfo->setNumberOfSelectedShapes (0);
mySelectionObserver.Clear();
return;
}
if (myMeasurementSize < 0) {
myMeasurementSize = ComputeMeasurementSize (myModel);
}
for (auto& aMeasurement : myMeasurements) {
aMeasurement.SetSize (myMeasurementSize);
auto aSceneNode = aFactory.
CreateNode (aMeasurement);
}
myScene.RemoveRoot (myRootMeasurementsNode);
myScene.AddRoot (aTempRootMeasureNode);
myScene.Update();
myRootMeasurementsNode = aTempRootMeasureNode;
mySelectionObserver.Clear();
}
void MeasurementsApplication::onInitialized()
{
connect (myMainWindow, SIGNAL (measurementModeChanged (const QVariant&)),
this, SLOT (onMeasurementModeChanged (const QVariant&)));
connect (myMainWindow, SIGNAL (measurementAngleUnitsChanged (const QVariant&)),
this, SLOT (onMeasurementAngleUnitsChanged (const QVariant&)));
connect (myMainWindow, SIGNAL (measurementLengthUnitsChanged (const QVariant&)),
this, SLOT (onMeasurementLengthUnitsChanged (const QVariant&)));
myMainWindow->setProperty ("model", QVariant::fromValue (myMeasurementInfo));
}
void MeasurementsApplication::Clear()
{
myMeasurementSize = -1.;
myMeasurementInfo->setNumberOfSelectedShapes (0);
myMeasurementInfo->setMeasurementsResults("");
myScene.RemoveRoot (myRootMeasurementsNode);
BaseViewerApplication::Clear();
}
MeasurementsWindow.qml
import QtQuick 2.12
import QtQuick.Controls 2.3
import CadEx 1.2
BaseViewerWindow {
signal measurementModeChanged (var index)
signal measurementAngleUnitsChanged (var index)
signal measurementLengthUnitsChanged (var index)
property var model: null
property int currentMeasurementMode: -1
property int numberOfRequiredShapes: 0
enum MeasurementType {
Distance,
Radius,
Diameter,
AngleBetweenPlanes,
AngleBetweenVertexes
}
function isAngleMeasure()
{
switch (currentMeasurementMode) {
case MeasurementsWindow.MeasurementType.Distance:
case MeasurementsWindow.MeasurementType.Radius:
case MeasurementsWindow.MeasurementType.Diameter:
return false;
case MeasurementsWindow.MeasurementType.AngleBetweenPlanes:
case MeasurementsWindow.MeasurementType.AngleBetweenVertexes:
return true;
}
}
function getShapesTypesString()
{
switch (currentMeasurementMode) {
case MeasurementsWindow.MeasurementType.Distance:
return "vertex, edge or face";
case MeasurementsWindow.MeasurementType.Radius:
case MeasurementsWindow.MeasurementType.Diameter:
return "edge or face";
case MeasurementsWindow.MeasurementType.AngleBetweenPlanes:
return "face";
case MeasurementsWindow.MeasurementType.AngleBetweenVertexes:
return "vertex";
}
}
function getNumberOfRequiredShapes()
{
switch (currentMeasurementMode) {
case MeasurementsWindow.MeasurementType.Distance:
return 2;
case MeasurementsWindow.MeasurementType.Radius:
case MeasurementsWindow.MeasurementType.Diameter:
return 1;
case MeasurementsWindow.MeasurementType.AngleBetweenPlanes:
return 2;
case MeasurementsWindow.MeasurementType.AngleBetweenVertexes:
return 3;
}
}
Menu {
id: measurementsTypeMenu
title: qsTr ("&Measurements")
property string currentMenuItemName: ""
width: angleBetweenVertexesMenuItem.width
ButtonGroup {
id: buttonGroup
buttons: measurementsTypeMenu.contentChildren
onCheckedButtonChanged: {
measurementsTypeMenu.currentMenuItemName = checkedButton.text;
currentMeasurementMode = checkedButton.mode;
}
}
MenuItem {
id: distanceMenuItem
text: qsTr ("Distance")
checkable: true
property var mode: MeasurementsWindow.MeasurementType.Distance
}
MenuItem {
id: radiusMenuItem
text: qsTr ("Radius")
checkable: true
property var mode: MeasurementsWindow.MeasurementType.Radius
}
MenuItem {
id: diameterMenuItem
text: qsTr ("Diameter")
checkable: true
property var mode: MeasurementsWindow.MeasurementType.Diameter;
}
MenuItem {
id: angleBetweenPlanesMenuItem
text: qsTr ("Angle between planes")
checkable: true
property var mode: MeasurementsWindow.MeasurementType.AngleBetweenPlanes
}
MenuItem {
id: angleBetweenVertexesMenuItem
text: qsTr ("Angle between vertexes")
checkable: true
property var mode: MeasurementsWindow.MeasurementType.AngleBetweenVertexes
}
}
onMenuCompleted: {
menuBarItem.addMenu (measurementsTypeMenu);
}
onCurrentMeasurementModeChanged: {
numberOfRequiredShapes = getNumberOfRequiredShapes();
model.numberOfSelectedShapes = 0;
measurementModeChanged (currentMeasurementMode);
}
Rectangle {
id: measurementBar
visible: currentMeasurementMode != -1
radius: 3
width: 300
height: column.implicitHeight
anchors.margins: 10
anchors.top: parent.top
anchors.right: parent.right
color: "#CCFFFFFF"
border.color: "#55000000"
readonly property string textColor: "#BB000000"
Column {
id: column
padding: 20
spacing: 20
width: parent.width
Text {
width: parent.width - 40
horizontalAlignment: Text.AlignHCenter
font.pointSize: 14
font.bold: true
color: measurementBar.textColor
text: measurementsTypeMenu.currentMenuItemName
}
Text {
visible: numberOfRequiredShapes != 0 && model.numberOfSelectedShapes !== numberOfRequiredShapes
text: !model ? " " : !model.numberOfSelectedShapes ? qsTr ("Select ") + getShapesTypesString()
: qsTr ("Selected ") + model.numberOfSelectedShapes +
qsTr (" of ") + numberOfRequiredShapes
font.pointSize: 13
font.italic: true
color: measurementBar.textColor
}
Text {
id: resultText
visible: !model ? "" : !model.numberOfSelectedShapes !== 0
&& model.numberOfSelectedShapes === numberOfRequiredShapes
&& model.measurementsResults !== ""
property string units: isAngleMeasure() ? "" : lengthUnitComboBox.currentText
text: !model ? "" : qsTr ("Result: ") + model.measurementsResults + units
font.pointSize: 13
color: measurementBar.textColor
}
Row {
visible: resultText.visible
Text {
text: qsTr ("Units: ")
font.pointSize: 13
color: measurementBar.textColor
anchors.verticalCenter: parent.verticalCenter
}
ComboBox {
id: lengthUnitComboBox
visible: resultText.visible && !isAngleMeasure()
model: ["mm", "cm", "m", "in", "ft", "yd"]
onCurrentIndexChanged: measurementLengthUnitsChanged (currentIndex);
}
ComboBox {
currentIndex: 1
visible: resultText.visible && isAngleMeasure()
model: ["rad", "deg"]
onCurrentIndexChanged: measurementAngleUnitsChanged (currentIndex);
}
}
}
}
}
main.cxx
#include "MeasurementsApplication.hxx"
#include <cadex/LicenseManager_Activate.h>
#include <QtCore/QUrl>
#include <QtWidgets/QApplication>
#include "../../../cadex_license.cxx"
int main (int argc, char *argv[])
{
auto aKey = LicenseKey::Value();
if (!CADExLicense_Activate (aKey)) {
qCritical ("Failed to activate CAD Exchanger license.");
return 1;
}
QCoreApplication::setAttribute (Qt::AA_EnableHighDpiScaling);
QApplication app (argc, argv);
MeasurementsApplication anApp;
if (anApp.Initialize (QUrl ("qrc:/qml/MeasurementsWindow.qml"), "viewPort")) {
return app.exec();
}
return 0;
}