From 00eccc380ce552f949d5da63d8bf2bc56c9fa906 Mon Sep 17 00:00:00 2001 From: blobfish Date: Tue, 17 Dec 2013 09:45:20 -0500 Subject: [PATCH] Part Dimension: new implementation files --- src/Mod/Part/Gui/TaskDimension.cpp | 1673 ++++++++++++++++++++++++++++ src/Mod/Part/Gui/TaskDimension.h | 360 ++++++ 2 files changed, 2033 insertions(+) create mode 100644 src/Mod/Part/Gui/TaskDimension.cpp create mode 100644 src/Mod/Part/Gui/TaskDimension.h diff --git a/src/Mod/Part/Gui/TaskDimension.cpp b/src/Mod/Part/Gui/TaskDimension.cpp new file mode 100644 index 0000000000..7eb926d7ac --- /dev/null +++ b/src/Mod/Part/Gui/TaskDimension.cpp @@ -0,0 +1,1673 @@ +/*************************************************************************** + * Copyright (c) 2013 Thomas Anderson * + * * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PreCompiled.h" +#include +#include +#include "../App/PartFeature.h" +#include +#include +#include +#include +#include +#include +#include + +#include "TaskDimension.h" + +bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub) +{ + App::Document *docPointer = App::GetApplication().getDocument(doc.c_str()); + if (!docPointer) + return false; + App::DocumentObject *objectPointer = docPointer->getObject(object.c_str()); + if (!objectPointer) + return false; + Part::Feature *feature = dynamic_cast(objectPointer); + if (!feature) + return false; + shapeOut = feature->Shape.getValue(); + if (sub.size() > 0) + shapeOut = feature->Shape.getShape().getSubShape(sub.c_str()); + if (shapeOut.IsNull()) + return false; + return true; +} + +bool PartGui::evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2) +{ + std::vector selections = Gui::Selection().getSelection(); + if (selections.size() != 2) + return false; + std::vector::iterator it; + std::vector shapes; + + for (it = selections.begin(); it != selections.end(); ++it) + { + Part::Feature *feature = dynamic_cast((*it).pObject); + if (!feature) + break; + TopoDS_Shape shape = feature->Shape.getValue(); + if (strlen((*it).SubName) > 0) + shape = feature->Shape.getShape().getSubShape((*it).SubName); + if (shape.IsNull()) + break; + shapes.push_back(shape); + } + + if (shapes.size() != 2) + return false; + + shape1 = shapes.front(); + shape2 = shapes.back(); + + return true; +} + +void PartGui::goDimensionLinearRoot() +{ + PartGui::ensureSomeDimensionVisible(); + + TopoDS_Shape shape1, shape2; + if(evaluateLinearPreSelection(shape1, shape2)) + { + goDimensionLinearNoTask(shape1, shape2); + Gui::Selection().clearSelection(); + } + else + { + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (!dlg) + { + Gui::Selection().clearSelection(); + dlg = new PartGui::TaskMeasureLinear(); + } + Gui::Control().showDialog(dlg); + } +} + +void PartGui::goDimensionLinearNoTask(const TopoDS_Shape &shape1, const TopoDS_Shape &shape2) +{ + //Warning: BRepExtrema_DistShapeShape solution array is NOT 0 based. + BRepExtrema_DistShapeShape measure(shape1, shape2); + if (!measure.IsDone() || measure.NbSolution() < 1) + return; + + dumpLinearResults(measure); + addLinearDimensions(measure); + + //if we ever make this a class add viewer to member. + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; +} + +void PartGui::dumpLinearResults(const BRepExtrema_DistShapeShape &measure) +{ + std::ostringstream out; + //switch to initializer list when switch to c++11 + std::vector typeNames; + typeNames.resize(3); + typeNames[0] = "Vertex"; + typeNames[1] = "Edge"; + typeNames[2] = "Face"; + + Base::Quantity quantity(measure.Value(), Base::Unit::Length); + out << std::endl<< std::setprecision(std::numeric_limits::digits10 + 1) << "distance = " << + measure.Value() << "mm unit distance = " << quantity.getUserString().toUtf8().constData() << std::endl << + "solution count: " << measure.NbSolution() << std::endl; + + for (int index = 1; index < measure.NbSolution() + 1; ++index) //not zero based. + { + gp_Pnt point1 = measure.PointOnShape1(index); + gp_Pnt point2 = measure.PointOnShape2(index); + out << " solution " << index << ":" << std::endl << std::setprecision(std::numeric_limits::digits10 + 1) << + " point1 " << point1.X() << " " << point1.Y() << " " << point1.Z() << std::endl << + " point2 " << point2.X() << " " << point2.Y() << " " << point2.Z() << std::endl << + " DeltaX " << fabs(point2.X() - point1.X()) << std::endl << + " DeltaY " << fabs(point2.Y() - point1.Y()) << std::endl << + " DeltaZ " << fabs(point2.Z() - point1.Z()) << std::endl << + " shape type on object1 is: " << typeNames.at(measure.SupportTypeShape1(index)) << std::endl << + " shape type on object2 is: " << typeNames.at(measure.SupportTypeShape2(index)) << std::endl; + } + out << std::endl; + Base::Console().Message(out.str().c_str()); +} + +Gui::View3DInventorViewer * PartGui::getViewer() +{ + Gui::View3DInventor *view = dynamic_cast(Gui::Application::Instance-> + activeDocument()->getActiveView()); + if (!view) + return 0; + Gui::View3DInventorViewer *viewer = view->getViewer(); + if (!viewer) + return 0; + return viewer; +} + +void PartGui::addLinearDimensions(const BRepExtrema_DistShapeShape &measure) +{ + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; + for (int index = 1; index < measure.NbSolution() + 1; ++index) + { + gp_Pnt point1 = measure.PointOnShape1(index); + gp_Pnt point2 = measure.PointOnShape2(index); + viewer->addDimension3d(createLinearDimension(point1, point2, SbColor(1.0, 0.0, 0.0))); + + //create deltas. point1 will always be the same. + gp_Pnt temp = point1; + gp_Pnt lastTemp = temp; + temp.SetX(point2.X()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + lastTemp = temp; + temp.SetY(point2.Y()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + lastTemp = temp; + temp.SetZ(point2.Z()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + } +} + +SoNode* PartGui::createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color) +{ + SbVec3f vec1(point1.X(), point1.Y(), point1.Z()); + SbVec3f vec2(point2.X(), point2.Y(), point2.Z()); + if ((vec2-vec1).length() < FLT_EPSILON) + return new SoSeparator(); //empty object. + PartGui::DimensionLinear *dimension = new PartGui::DimensionLinear(); + dimension->point1.setValue(vec1); + dimension->point2.setValue(vec2); + + Base::Quantity quantity(static_cast((vec2-vec1).length()), Base::Unit::Length); + dimension->text.setValue(quantity.getUserString().toUtf8().constData()); + + dimension->dColor.setValue(color); + return dimension; +} + +void PartGui::eraseAllDimensions() +{ + Gui::View3DInventor *view = dynamic_cast(Gui::Application::Instance-> + activeDocument()->getActiveView()); + if (!view) + return; + Gui::View3DInventorViewer *viewer = view->getViewer(); + if (!viewer) + return; + viewer->eraseAllDimensions(); +} + +void PartGui::toggle3d() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("Dimensions3dVisible", true); + if (visibility) + group->SetBool("Dimensions3dVisible", false); + else + group->SetBool("Dimensions3dVisible", true); +} + +void PartGui::toggleDelta() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("DimensionsDeltaVisible", true); + if (visibility) + group->SetBool("DimensionsDeltaVisible", false); + else + group->SetBool("DimensionsDeltaVisible", true); +} + +void PartGui::ensureSomeDimensionVisible() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibilityAll = group->GetBool("DimensionsVisible", true); + if (!visibilityAll) + group->SetBool("DimensionsVisible", true); + bool visibility3d = group->GetBool("Dimensions3dVisible", true); + bool visibilityDelta = group->GetBool("DimensionsDeltaVisible", true); + + if (!visibility3d && !visibilityDelta) //both turned off. + group->SetBool("Dimensions3dVisible", true); //turn on 3d, so something is visible. +} + +void PartGui::ensure3dDimensionVisible() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibilityAll = group->GetBool("DimensionsVisible", true); + if (!visibilityAll) + group->SetBool("DimensionsVisible", true); + bool visibility3d = group->GetBool("Dimensions3dVisible", true); + + if (!visibility3d) //both turned off. + group->SetBool("Dimensions3dVisible", true); //turn on 3d, so something is visible. +} + + +SO_KIT_SOURCE(PartGui::DimensionLinear); + +void PartGui::DimensionLinear::initClass() +{ + SO_KIT_INIT_CLASS(DimensionLinear, SoSeparatorKit, "SeparatorKit"); +} + +PartGui::DimensionLinear::DimensionLinear() +{ + SO_KIT_CONSTRUCTOR(PartGui::DimensionLinear); + + SO_KIT_ADD_CATALOG_ENTRY(transformation, SoTransform, TRUE, topSeparator, , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(annotate, SoAnnotation, TRUE, topSeparator, , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(leftArrow, SoShapeKit, TRUE, topSeparator, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(rightArrow, SoShapeKit, TRUE, topSeparator, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(line, SoShapeKit, TRUE, annotate, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(textSep, SoSeparator, TRUE, annotate, ,TRUE); + + SO_KIT_INIT_INSTANCE(); + + SO_NODE_ADD_FIELD(rotate, (1.0, 0.0, 0.0, 0.0));//postion orientation of the dimension. + SO_NODE_ADD_FIELD(length, (1.0));//turns into dimension length + SO_NODE_ADD_FIELD(origin, (0.0, 0.0, 0.0));//static + SO_NODE_ADD_FIELD(text, ("test"));//dimension text + SO_NODE_ADD_FIELD(dColor, (1.0, 0.0, 0.0));//dimension color. + + point1.setValue(SbVec3f(0.0, 0.0, 0.0)); + point2.setValue(SbVec3f(1.0, 0.0, 0.0)); + + setupDimension(); +} + +PartGui::DimensionLinear::~DimensionLinear() +{ + +} + +SbBool PartGui::DimensionLinear::affectsState() const +{ + return FALSE; +} + +void PartGui::DimensionLinear::setupDimension() +{ + //transformation + SoTransform *trans = static_cast(getPart("transformation", TRUE)); + trans->translation.connectFrom(&point1); + //build engine for vector subtraction and length. + SoCalculator *hyp = new SoCalculator(); + hyp->A.connectFrom(&point1); + hyp->B.connectFrom(&point2); + hyp->expression.set1Value(0, "oA = B-A"); + hyp->expression.set1Value(1, "oB = normalize(oA)"); + hyp->expression.set1Value(2, "oa = length(oA)"); + length.connectFrom(&hyp->oa); + //build engine for rotation. + SoComposeRotationFromTo *rotationEngine = new SoComposeRotationFromTo(); + rotationEngine->from.setValue(SbVec3f(1.0, 0.0, 0.0)); + rotationEngine->to.connectFrom(&hyp->oB); + trans->rotation.connectFrom(&rotationEngine->rotation); + + //color + SoMaterial *material = new SoMaterial; + material->diffuseColor.connectFrom(&dColor); + + //dimension arrows. + SoCone *cone = new SoCone(); + cone->bottomRadius.setValue(0.25); + cone->height.setValue(0.5); + + setPart("leftArrow.shape", cone); + set("leftArrow.transform", "rotation 0.0 0.0 1.0 1.5707963"); + set("leftArrow.transform", "translation 0.25 0.0 0.0"); //half cone height. + setPart("rightArrow.shape", cone); + set("rightArrow.transform", "rotation 0.0 0.0 -1.0 1.5707963"); //no constant for PI. + //have use local here to do the offset because the main is wired up to length of dimension. + set("rightArrow.localTransform", "translation 0.0 -0.25 0.0"); //half cone height. + + SoTransform *transform = static_cast(getPart("rightArrow.transform", FALSE)); + if (!transform) + return;//what to do here? + SoComposeVec3f *vec = new SoComposeVec3f; + vec->x.connectFrom(&length); + vec->y.setValue(0.0); + vec->z.setValue(0.0); + transform->translation.connectFrom(&vec->vector); + + setPart("leftArrow.material", material); + setPart("rightArrow.material", material); + + //line + SoConcatenate *catEngine = new SoConcatenate(SoMFVec3f::getClassTypeId()); + //don't know how to get around having this dummy origin. cat engine wants to connectfrom? + catEngine->input[0]->connectFrom(&origin); + catEngine->input[1]->connectFrom(&vec->vector); + + SoVertexProperty *lineVerts = new SoVertexProperty; + lineVerts->vertex.connectFrom(catEngine->output); + + int lineVertexMap[] = {0, 1}; + int lineVertexMapSize(sizeof(lineVertexMap)/sizeof(int)); + SoIndexedLineSet *line = new SoIndexedLineSet; + line->vertexProperty = lineVerts; + line->coordIndex.setValues(0, lineVertexMapSize, lineVertexMap); + + setPart("line.shape", line); + setPart("line.material", material); + + //text + SoSeparator *textSep = static_cast(getPart("textSep", TRUE)); + if (!textSep) + return; + + textSep->addChild(material); + + SoCalculator *textVecCalc = new SoCalculator(); + textVecCalc->A.connectFrom(&vec->vector); + textVecCalc->B.set1Value(0, 0.0, 0.250, 0.0); + textVecCalc->expression.set1Value(0, "oA = (A / 2) + B"); + + SoTransform *textTransform = new SoTransform(); + textTransform->translation.connectFrom(&textVecCalc->oA); + textSep->addChild(textTransform); + + SoFont *fontNode = new SoFont(); + fontNode->name.setValue("defaultFont"); + fontNode->size.setValue(30); + textSep->addChild(fontNode); + + SoText2 *textNode = new SoText2(); + textNode->justification = SoText2::CENTER; + textNode->string.connectFrom(&text); + textSep->addChild(textNode); + + //this prevents the 2d text from screwing up the bounding box for a viewall + SoResetTransform *rTrans = new SoResetTransform; + rTrans->whatToReset = SoResetTransform::BBOX; + textSep->addChild(rTrans); +} + +PartGui::TaskMeasureLinear::TaskMeasureLinear(): selections1(), selections2(), buttonSelectedIndex(0) +{ + setUpGui(); +} + +PartGui::TaskMeasureLinear::~TaskMeasureLinear() +{ + Gui::Selection().clearSelection(); +} + +void PartGui::TaskMeasureLinear::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (buttonSelectedIndex == 0) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + selections1.selections.clear();//we only want one item. + selections1.selections.push_back(newSelection); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + } + if (buttonSelectedIndex == 1) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + selections2.selections.clear();//we only want one item. + selections2.selections.push_back(newSelection); + buildDimension(); + clearSelectionStrings(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + } +} + +void PartGui::TaskMeasureLinear::selectionClearDelayedSlot() +{ + //hack. + //clearing selections are not working as I hoped. Apparently the observer callback gets called + //before the actual selection takes place. Resulting in selections being left. this addresses this + //by being called from the event loop. + this->blockConnection(true); + Gui::Selection().clearSelection(); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::buildDimension() +{ + if(selections1.selections.size() != 1 || selections2.selections.size() != 1) + return; + + DimSelections::DimSelection current1 = selections1.selections.at(0); + DimSelections::DimSelection current2 = selections2.selections.at(0); + + TopoDS_Shape shape1, shape2; + if (!getShapeFromStrings(shape1, current1.documentName, current1.objectName, current1.subObjectName)) + { + Base::Console().Message("\nFailed to get shape\n\n"); + return; + } + if (!getShapeFromStrings(shape2, current2.documentName, current2.objectName, current2.subObjectName)) + { + Base::Console().Message("\nFailed to get shape\n\n"); + return; + } + goDimensionLinearNoTask(shape1, shape2); +} + +void PartGui::TaskMeasureLinear::clearSelectionStrings() +{ + selections1.selections.clear(); + selections2.selections.clear(); +} + +void PartGui::TaskMeasureLinear::setUpGui() +{ + QPixmap mainIcon = Gui::BitmapFactory().pixmap("Part_Measure_Linear"); + + Gui::TaskView::TaskBox* selectionTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Selections"), false, 0); + QVBoxLayout *selectionLayout = new QVBoxLayout(); + stepped = new SteppedSelection(2, selectionTaskBox); + selectionLayout->addWidget(stepped); + selectionTaskBox->groupLayout()->addLayout(selectionLayout); + + Gui::TaskView::TaskBox* controlTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Control"), false, 0); + QVBoxLayout *controlLayout = new QVBoxLayout(); + + QHBoxLayout *resetLayout = new QHBoxLayout(); + resetLayout->addSpacing(5); //hack for alignment. + QPushButton *resetButton = new QPushButton(mainIcon, QObject::tr("Reset Dialog"), controlTaskBox); + resetLayout->addWidget(resetButton); + resetLayout->addStretch(); + controlLayout->addLayout(resetLayout); + QObject::connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); + + DimensionControl *control = new DimensionControl(controlTaskBox); + controlLayout->addWidget(control); + controlTaskBox->groupLayout()->addLayout(controlLayout); + + this->setButtonPosition(TaskDialog::South); + Content.push_back(selectionTaskBox); + Content.push_back(controlTaskBox); + + stepped->getButton(0)->setChecked(true);//before wired up. + QObject::connect(stepped->getButton(0), SIGNAL(toggled(bool)), this, SLOT(selection1Slot(bool))); + QObject::connect(stepped->getButton(1), SIGNAL(toggled(bool)), this, SLOT(selection2Slot(bool))); +} + +void PartGui::TaskMeasureLinear::selection1Slot(bool checked) +{ + if (!checked) + { + if (selections1.selections.size() > 0) + stepped->setIconDone(0); + return; + } + buttonSelectedIndex = 0; + + this->blockConnection(true); + Gui::Selection().clearSelection(); + //we should only be working with 1 entity, but oh well do the loop anyway. + std::vector::const_iterator it; + for (it = selections1.selections.begin(); it != selections1.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::selection2Slot(bool checked) +{ + if (!checked) + return; + buttonSelectedIndex = 1; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections2.selections.begin(); it != selections2.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::resetDialogSlot(bool) +{ + clearSelectionStrings(); + this->blockConnection(true); + Gui::Selection().clearSelection(); + stepped->getButton(0)->setChecked(true); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::TaskMeasureLinear::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::TaskMeasureLinear::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +PartGui::VectorAdapter::VectorAdapter() : status(false), vector() +{ +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn) : + status(false), vector(), origin(pickedPointIn) +{ + Handle_Geom_Surface surface = BRep_Tool::Surface(faceIn); + if (surface->IsKind(STANDARD_TYPE(Geom_ElementarySurface))) + { + Handle_Geom_ElementarySurface eSurface = Handle(Geom_ElementarySurface)::DownCast(surface); + gp_Dir direction = eSurface->Axis().Direction(); + vector = direction; + vector.Normalize(); + if (faceIn.Orientation() == TopAbs_REVERSED) + vector.Reverse(); + if (surface->IsKind(STANDARD_TYPE(Geom_CylindricalSurface)) || + surface->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) + ) + { + origin = eSurface->Axis().Location().XYZ(); + projectOriginOntoVector(pickedPointIn); + } + else + origin = pickedPointIn + vector; + status = true; + } +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn) : + status(false), vector(), origin(pickedPointIn) +{ + TopoDS_Vertex firstVertex = TopExp::FirstVertex(edgeIn, Standard_True); + TopoDS_Vertex lastVertex = TopExp::LastVertex(edgeIn, Standard_True); + vector = PartGui::convert(lastVertex) - PartGui::convert(firstVertex); + if (vector.Magnitude() < Precision::Confusion()) + return; + vector.Normalize(); + + status = true; + projectOriginOntoVector(pickedPointIn); +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In) : + status(false), vector(), origin() +{ + vector = PartGui::convert(vertex2In) - PartGui::convert(vertex1In); + vector.Normalize(); + + //build origin half way. + gp_Vec tempVector = (PartGui::convert(vertex2In) - PartGui::convert(vertex1In)); + double mag = tempVector.Magnitude(); + tempVector.Normalize(); + tempVector *= (mag / 2.0); + origin = tempVector + PartGui::convert(vertex1In); + + status = true; +} + +PartGui::VectorAdapter::VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2) : + status(false), vector(), origin() +{ + vector = vector2- vector1; + vector.Normalize(); + + //build origin half way. + gp_Vec tempVector = vector2 - vector1; + double mag = tempVector.Magnitude(); + tempVector.Normalize(); + tempVector *= (mag / 2.0); + origin = tempVector + vector1; + + status = true; +} + +void PartGui::VectorAdapter::projectOriginOntoVector(const gp_Vec &pickedPointIn) +{ + Handle_Geom_Curve heapLine = new Geom_Line(origin.XYZ(), vector.XYZ()); + gp_Pnt tempPoint(pickedPointIn.XYZ()); + GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine); + if (projection.NbPoints() < 1) + return; + origin.SetXYZ(projection.Point(1).XYZ()); +} + +PartGui::VectorAdapter::operator gp_Lin() const +{ + gp_Pnt tempOrigin; + tempOrigin.SetXYZ(origin.XYZ()); + return gp_Lin(tempOrigin, gp_Dir(vector)); +} + +gp_Vec PartGui::convert(const TopoDS_Vertex &vertex) +{ + gp_Pnt point = BRep_Tool::Pnt(vertex); + gp_Vec out(point.X(), point.Y(), point.Z()); + return out; +} + +void PartGui::goDimensionAngularRoot() +{ + PartGui::ensure3dDimensionVisible(); + + VectorAdapter adapter1, adapter2; + if(PartGui::evaluateAngularPreSelection(adapter1, adapter2)) + goDimensionAngularNoTask(adapter1, adapter2); + else + { + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (!dlg) + { + Gui::Selection().clearSelection(); + dlg = new PartGui::TaskMeasureAngular(); + } + Gui::Control().showDialog(dlg); + } + Gui::Selection().clearSelection(); +} + +bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out) +{ + std::vector selections = Gui::Selection().getSelection(); + if (selections.size() > 4 || selections.size() < 2) + return false; + std::vector::iterator it; + std::vector adapters; + TopoDS_Vertex lastVertex; + for (it = selections.begin(); it != selections.end(); ++it) + { + Part::Feature *feature = dynamic_cast((*it).pObject); + if (!feature) + break; + TopoDS_Shape shape = feature->Shape.getValue(); + if (strlen((*it).SubName) > 0) + shape = feature->Shape.getShape().getSubShape((*it).SubName); + if (shape.IsNull()) + break; + + if (shape.ShapeType() == TopAbs_VERTEX) + { + TopoDS_Vertex currentVertex = TopoDS::Vertex(shape); + if (!lastVertex.IsNull()) + { + //need something here for 0 length vector. + //create a point half way between to vertices. + adapters.push_back(VectorAdapter(currentVertex, lastVertex)); + lastVertex = TopoDS_Vertex(); + } + else + { + lastVertex = currentVertex; + } + continue; + } + //vertices have to be selected in succession. so if we make it here clear the last vertex. + lastVertex = TopoDS_Vertex(); + + gp_Vec pickPoint(it->x, it->y, it->z); + //can't use selections without a pick point. + if (pickPoint.IsEqual(gp_Vec(0.0, 0.0, 0.0), Precision::Confusion(), Precision::Angular())) + { + Base::Console().Message("Can't use selections without a pick point.\n"); + continue; + } + + if (shape.ShapeType() == TopAbs_EDGE) + { + TopoDS_Edge edge = TopoDS::Edge(shape); + // make edge orientation so that end of edge closest to pick is head of vector. + gp_Vec firstPoint = PartGui::convert(TopExp::FirstVertex(edge, Standard_True)); + gp_Vec lastPoint = PartGui::convert(TopExp::LastVertex(edge, Standard_True)); + double firstDistance = (firstPoint - pickPoint).Magnitude(); + double lastDistance = (lastPoint - pickPoint).Magnitude(); + if (lastDistance > firstDistance) + { + if (edge.Orientation() == TopAbs_FORWARD) + edge.Orientation(TopAbs_REVERSED); + else + edge.Orientation(TopAbs_FORWARD); + } + adapters.push_back(VectorAdapter(edge, pickPoint)); + continue; + } + + if (shape.ShapeType() == TopAbs_FACE) + { + TopoDS_Face face = TopoDS::Face(shape); + adapters.push_back(VectorAdapter(face, pickPoint)); + continue; + } + } + + if (adapters.size() != 2) + return false; + if (!adapters.front().isValid() || !adapters.back().isValid()) + return false; + + vector1Out = adapters.front(); + vector2Out = adapters.back(); + + //making sure pick points are not equal + if ((vector1Out.getPickPoint() - vector2Out.getPickPoint()).Magnitude() < std::numeric_limits::epsilon()) + { + Base::Console().Message("pick points are equal\n"); + return false; + } + + return true; +} + +void PartGui::goDimensionAngularNoTask(const VectorAdapter &vector1Adapter, const VectorAdapter &vector2Adapter) +{ + gp_Vec vector1 = vector1Adapter; + gp_Vec vector2 = vector2Adapter; + double angle = vector1.Angle(vector2); + + std::ostringstream stream; + stream << std::setprecision(std::numeric_limits::digits10 + 1) << std::fixed << std::endl << + "angle in radians is: " << angle << std::endl << + "angle in degrees is: " << 180 * angle / M_PI << std::endl; + if (angle < M_PI / 2.0d) + stream << std::setprecision(std::numeric_limits::digits10 + 1) << + "complement in radians is: " << M_PI / 2.0d - angle << std::endl << + "complement in degrees is: " << 90 - 180 * angle / M_PI << std::endl; + //I don't think we get anything over 180, but just in case. + if (angle > M_PI / 2.0d && angle < M_PI) + stream << std::setprecision(std::numeric_limits::digits10 + 1) << + "supplement in radians is: " << M_PI - angle << std::endl << + "supplement in degrees is: " << 180 - 180 * angle / M_PI << std::endl; + Base::Console().Message(stream.str().c_str()); + + SbMatrix dimSys; + double radius; + double displayAngle;//have to fake the angle in the 3d. + + if (vector1.IsParallel(vector2, Precision::Angular())) + { + //take first point project it onto second vector. + Handle_Geom_Curve heapLine2 = new Geom_Line(vector2Adapter); + gp_Pnt tempPoint(vector1Adapter.getPickPoint().XYZ()); + + GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine2); + if (projection.NbPoints() < 1) + { + Base::Console().Message("parallel vectors: couldn't project onto line\n"); + return; + } + gp_Vec newPoint2; + newPoint2.SetXYZ(projection.Point(1).XYZ()); + + //if points are colinear, projection doesn't work and returns the same point. + //In this case we just use the original point. + if ((newPoint2 - vector1Adapter.getPickPoint()).Magnitude() < Precision::Confusion()) + newPoint2 = vector2Adapter.getPickPoint(); + + //now get midpoint between for dim origin. + gp_Vec point1 = vector1Adapter.getPickPoint(); + gp_Vec midPointProjection = newPoint2 - point1; + double distance = midPointProjection.Magnitude(); + midPointProjection.Normalize(); + midPointProjection *= distance / 2.0d; + + gp_Vec origin = point1 + midPointProjection; + + //yaxis should be the same as vector1, but doing this to eliminate any potential slop from + //using precision::angular. If lines are colinear and we have no plane, we can't establish zAxis from crossing. + //we just the absolute axis. + gp_Vec xAxis = (point1 - origin).Normalized(); + gp_Vec zAxis; + if (xAxis.IsParallel(vector1, Precision::Angular())) + { + if (!xAxis.IsParallel(gp_Vec(0.0d, 0.0d, 1.0d), Precision::Angular())) + zAxis = gp_Vec(0.0d, 0.0d, 1.0d); + else + zAxis = gp_Vec(0.0d, 1.0d, 0.0d); + } + else + zAxis = xAxis.Crossed(vector1).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + zAxis = xAxis.Crossed(yAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), origin.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), origin.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), origin.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + dimSys = dimSys.transpose(); + + radius = midPointProjection.Magnitude(); + displayAngle = M_PI; + } + else + { + Handle_Geom_Curve heapLine1 = new Geom_Line(vector1Adapter); + Handle_Geom_Curve heapLine2 = new Geom_Line(vector2Adapter); + + GeomAPI_ExtremaCurveCurve extrema(heapLine1, heapLine2); + + if (extrema.NbExtrema() < 1) + { + Base::Console().Message("couldn't get extrema\n"); + return; + } + + gp_Pnt extremaPoint1, extremaPoint2, dimensionOriginPoint; + extrema.Points(1, extremaPoint1, extremaPoint2); + if (extremaPoint1.Distance(extremaPoint2) < Precision::Confusion()) + dimensionOriginPoint = extremaPoint1; + else + { + //find half way point in between extrema points for dimension origin. + gp_Vec vec1(extremaPoint1.XYZ()); + gp_Vec vec2(extremaPoint2.XYZ()); + gp_Vec connection(vec2-vec1); + Standard_Real distance = connection.Magnitude(); + connection.Normalize(); + connection *= (distance / 2.0d); + dimensionOriginPoint.SetXYZ((vec1 + connection).XYZ()); + } + + gp_Vec thirdPoint(vector2Adapter.getPickPoint()); + gp_Vec originVector(dimensionOriginPoint.XYZ()); + gp_Vec extrema2Vector(extremaPoint2.XYZ()); + radius = (vector1Adapter.getPickPoint() - originVector).Magnitude(); + double legOne = (extrema2Vector - originVector).Magnitude(); + displayAngle = angle; + if (legOne > Precision::Confusion()) + { + double legTwo = sqrt(pow(radius, 2) - pow(legOne, 2)); + gp_Vec projectionVector(vector2); + projectionVector.Normalize(); + projectionVector *= legTwo; + thirdPoint = extrema2Vector + projectionVector; + gp_Vec hyp(thirdPoint - originVector); + hyp.Normalize(); + gp_Vec otherSide(vector1Adapter.getPickPoint() - originVector); + otherSide.Normalize(); + displayAngle = hyp.Angle(otherSide); + } + + gp_Vec xAxis = (vector1Adapter.getPickPoint() - originVector).Normalized(); + gp_Vec fakeYAxis = (thirdPoint - originVector).Normalized(); + gp_Vec zAxis = (xAxis.Crossed(fakeYAxis)).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), dimensionOriginPoint.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), dimensionOriginPoint.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), dimensionOriginPoint.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + + dimSys = dimSys.transpose(); + } + + DimensionAngular *dimension = new DimensionAngular(); + dimension->matrix.setValue(dimSys); + dimension->radius.setValue(radius); + dimension->angle.setValue(static_cast(displayAngle)); + dimension->text.setValue((Base::Quantity(180 * angle / M_PI, Base::Unit::Angle)).getUserString().toUtf8().constData()); + dimension->dColor.setValue(SbColor(0.0, 0.0, 1.0)); + + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; + + viewer->addDimension3d(dimension); +} + +SO_KIT_SOURCE(PartGui::DimensionAngular); + +void PartGui::DimensionAngular::initClass() +{ + SO_KIT_INIT_CLASS(DimensionAngular, SoSeparatorKit, "SeparatorKit"); +} + +PartGui::DimensionAngular::DimensionAngular() +{ + SO_KIT_CONSTRUCTOR(PartGui::DimensionAngular); + + SO_KIT_ADD_CATALOG_ENTRY(transformation, SoMatrixTransform, TRUE, topSeparator, , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(annotate, SoAnnotation, TRUE, topSeparator, , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arrow1, SoShapeKit, TRUE, topSeparator, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arrow2, SoShapeKit, TRUE, topSeparator, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arcSep, SoSeparator, TRUE, annotate, ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(textSep, SoSeparator, TRUE, annotate, ,TRUE); + + SO_KIT_INIT_INSTANCE(); + + SO_NODE_ADD_FIELD(radius, (10.0)); + SO_NODE_ADD_FIELD(angle, (1.0)); + SO_NODE_ADD_FIELD(text, ("test"));//dimension text + SO_NODE_ADD_FIELD(dColor, (1.0, 0.0, 0.0));//dimension color. + SO_NODE_ADD_FIELD(matrix, (1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0)); + + setupDimension(); +} + +PartGui::DimensionAngular::~DimensionAngular() +{ + +} + +SbBool PartGui::DimensionAngular::affectsState() const +{ + return FALSE; +} + + +void PartGui::DimensionAngular::setupDimension() +{ + //transformation + SoMatrixTransform *trans = static_cast(getPart("transformation", TRUE)); + trans->matrix.connectFrom(&matrix); + + //color + SoMaterial *material = new SoMaterial; + material->diffuseColor.connectFrom(&dColor); + + //dimension arrows. + SoCone *cone = new SoCone(); + cone->bottomRadius.setValue(0.25); + cone->height.setValue(0.5); + + setPart("arrow1.shape", cone); + set("arrow1.localTransform", "rotation 0.0 0.0 1.0 3.1415927"); + set("arrow1.localTransform", "translation 0.0 0.25 0.0"); //half cone height. + setPart("arrow2.shape", cone); + set("arrow2.transform", "rotation 0.0 0.0 1.0 0.0"); + set("arrow2.localTransform", "translation 0.0 -0.25 0.0"); //half cone height. + + //I was getting errors if I didn't manually allocate for these transforms. Not sure why. + SoTransform *arrow1Transform = new SoTransform(); + SoComposeVec3f *arrow1Compose = new SoComposeVec3f(); + arrow1Compose->x.connectFrom(&radius); + arrow1Compose->y.setValue(0.0); + arrow1Compose->y.setValue(0.0); + arrow1Transform->translation.connectFrom(&arrow1Compose->vector); + setPart("arrow1.transform", arrow1Transform); + + SoComposeRotation *arrow2Rotation = new SoComposeRotation(); + arrow2Rotation->angle.connectFrom(&angle); + arrow2Rotation->axis.setValue(0.0, 0.0, 1.0); + SoTransform *arrow2Transform = new SoTransform(); + arrow2Transform->rotation.connectFrom(&arrow2Rotation->rotation); + SoCalculator *arrow2LocationCalc = new SoCalculator(); + arrow2LocationCalc->a.connectFrom(&angle); + arrow2LocationCalc->b.connectFrom(&radius); + arrow2LocationCalc->expression.set1Value(0, "oa = cos(a) * b"); //xLocation + arrow2LocationCalc->expression.set1Value(1, "ob = sin(a) * b"); //yLocation + SoComposeVec3f *arrow2Compose = new SoComposeVec3f(); + arrow2Compose->x.connectFrom(&arrow2LocationCalc->oa); + arrow2Compose->y.connectFrom(&arrow2LocationCalc->ob); + arrow2Compose->z.setValue(0.0f); + arrow2Transform->translation.connectFrom(&arrow2Compose->vector); + setPart("arrow2.transform", arrow2Transform); + + setPart("arrow1.material", material); + setPart("arrow2.material", material); + + ArcEngine *arcEngine = new ArcEngine(); + arcEngine->angle.connectFrom(&angle); + arcEngine->radius.connectFrom(&radius); + arcEngine->deviation.setValue(0.1); + + SoCoordinate3 *coordinates = new SoCoordinate3(); + coordinates->point.connectFrom(&arcEngine->points); + + SoLineSet *lineSet = new SoLineSet(); + lineSet->vertexProperty.setValue(coordinates); + lineSet->numVertices.connectFrom(&arcEngine->pointCount); + lineSet->startIndex.setValue(0); + + SoSeparator *arcSep = static_cast(getPart("arcSep", TRUE)); + if (!arcSep) + return; + arcSep->addChild(material); + arcSep->addChild(lineSet); + + //text + SoSeparator *textSep = static_cast(getPart("textSep", TRUE)); + if (!textSep) + return; + + textSep->addChild(material); + + SoCalculator *textVecCalc = new SoCalculator(); + textVecCalc->a.connectFrom(&angle); + textVecCalc->b.connectFrom(&radius); + textVecCalc->expression.set1Value(0, "oa = a / 2.0"); + textVecCalc->expression.set1Value(1, "ob = cos(oa) * b"); //x + textVecCalc->expression.set1Value(2, "oc = sin(oa) * b"); //y + + SoComposeVec3f *textLocation = new SoComposeVec3f(); + textLocation->x.connectFrom(&textVecCalc->ob); + textLocation->y.connectFrom(&textVecCalc->oc); + textLocation->z.setValue(0.0); + + + SoTransform *textTransform = new SoTransform(); + textTransform->translation.connectFrom(&textLocation->vector); + textSep->addChild(textTransform); + + SoFont *fontNode = new SoFont(); + fontNode->name.setValue("defaultFont"); + fontNode->size.setValue(30); + textSep->addChild(fontNode); + + SoText2 *textNode = new SoText2(); + textNode->justification = SoText2::CENTER; + textNode->string.connectFrom(&text); + textSep->addChild(textNode); + + //this prevents the 2d text from screwing up the bounding box for a viewall + SoResetTransform *rTrans = new SoResetTransform; + rTrans->whatToReset = SoResetTransform::BBOX; + textSep->addChild(rTrans); +} + +SO_ENGINE_SOURCE(PartGui::ArcEngine); + +PartGui::ArcEngine::ArcEngine() +{ + SO_ENGINE_CONSTRUCTOR(ArcEngine); + + SO_ENGINE_ADD_INPUT(radius, (10.0)); + SO_ENGINE_ADD_INPUT(angle, (1.0)); + SO_ENGINE_ADD_INPUT(deviation, (0.25)); + + SO_ENGINE_ADD_OUTPUT(points, SoMFVec3f); + SO_ENGINE_ADD_OUTPUT(pointCount, SoSFInt32); +} + +void PartGui::ArcEngine::initClass() +{ + SO_ENGINE_INIT_CLASS(ArcEngine, SoEngine, "Engine"); +} + +void PartGui::ArcEngine::evaluate() +{ + if (radius.getValue() < std::numeric_limits::epsilon() || + angle.getValue() < std::numeric_limits::epsilon() || + deviation.getValue() < std::numeric_limits::epsilon()) + { + defaultValues(); + return; + } + + float deviationAngle(acos((radius.getValue() - deviation.getValue()) / radius.getValue())); + std::vector tempPoints; + int segmentCount; + if (deviationAngle >= angle.getValue()) + segmentCount = 1; + else + { + segmentCount = static_cast(angle.getValue() / deviationAngle) + 1; + if (segmentCount < 2) + { + defaultValues(); + return; + } + } + float angleIncrement = angle.getValue() / static_cast(segmentCount); + for (int index = 0; index < segmentCount + 1; ++index) + { + SbVec3f currentNormal(1.0, 0.0, 0.0); + float currentAngle = index * angleIncrement; + SbRotation rotation(SbVec3f(0.0, 0.0, 1.0), currentAngle); + rotation.multVec(currentNormal, currentNormal); + tempPoints.push_back(currentNormal * radius.getValue()); + } + int tempCount = tempPoints.size(); //for macro. + SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(tempCount)); + SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(tempCount)); + std::vector::const_iterator it; + for (it = tempPoints.begin(); it != tempPoints.end(); ++it) + { + int currentIndex = it-tempPoints.begin(); //for macro. + SbVec3f temp(*it); //for macro + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(currentIndex, temp)); + } + +} + +void PartGui::ArcEngine::defaultValues() +{ + //just some non-failing info. + SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(2)); + SbVec3f point1(10.0, 0.0, 0.0); + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(0, point1)); + SbVec3f point2(7.07, 7.07, 0.0); + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(1, point2)); + SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(2)); +} + +PartGui::SteppedSelection::SteppedSelection(const uint& buttonCountIn, QWidget* parent): + QWidget(parent) +{ + if (buttonCountIn < 1) + return; + + QVBoxLayout *mainLayout = new QVBoxLayout(); + this->setLayout(mainLayout); + + QButtonGroup *buttonGroup = new QButtonGroup(); + buttonGroup->setExclusive(true); + + for (uint index = 0; index < buttonCountIn; ++index) + { + ButtonIconPairType tempPair; + + std::ostringstream stream; + stream << "Selection " << ((index < 10) ? "0" : "") << index + 1; + QString buttonText = QObject::tr(stream.str().c_str()); + QPushButton *button = new QPushButton(buttonText, this); + button->setCheckable(true); + buttonGroup->addButton(button); + connect(button, SIGNAL(toggled(bool)), this, SLOT(selectionSlot(bool))); + + QLabel *label = new QLabel; + + tempPair.first = button; + tempPair.second = label; + buttons.push_back(tempPair); + + QHBoxLayout *layout = new QHBoxLayout(); + mainLayout->addLayout(layout); + layout->addWidget(button); + layout->addSpacing(10); + layout->addWidget(label); + layout->addStretch(); + } + mainLayout->addStretch(); + + buildPixmaps(); //uses button size +} + +PartGui::SteppedSelection::~SteppedSelection() +{ + if(stepActive) + { + delete stepActive; + stepActive = 0; + } + if (stepDone) + { + delete stepDone; + stepDone = 0; + } +} + +void PartGui::SteppedSelection::buildPixmaps() +{ + assert(buttons.size() > 0); + int iconHeight(buttons.at(0).first->height()-6); + stepActive = new QPixmap(Gui::BitmapFactory().pixmap("Part_Measure_Step_Active").scaled + (iconHeight, iconHeight, Qt::KeepAspectRatio)); + stepDone = new QPixmap(Gui::BitmapFactory().pixmap("Part_Measure_Step_Done").scaled + (iconHeight, iconHeight, Qt::KeepAspectRatio)); +} + +void PartGui::SteppedSelection::selectionSlot(bool checked) +{ + QPushButton *sender = qobject_cast(QObject::sender()); + assert(sender != 0); + std::vector::iterator it; + for (it = buttons.begin(); it != buttons.end(); ++it) + if (it->first == sender) + break; + assert(it != buttons.end()); + + if (checked) + it->second->setPixmap(*stepActive); + else + it->second->setPixmap(QPixmap()); +} + +QPushButton* PartGui::SteppedSelection::getButton(const uint& index) +{ + return buttons.at(index).first; +} + +void PartGui::SteppedSelection::setIconDone(const uint& index) +{ + buttons.at(index).second->setPixmap(*stepDone); +} + +PartGui::DimensionControl::DimensionControl(QWidget* parent): QWidget(parent) +{ + QVBoxLayout *commandLayout = new QVBoxLayout(); + this->setLayout(commandLayout); + + QPushButton *toggle3dButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Toggle_3d"), + QObject::tr("Toggle 3d"), this); + QObject::connect(toggle3dButton, SIGNAL(clicked(bool)), this, SLOT(toggle3dSlot(bool))); + QHBoxLayout *layout = new QHBoxLayout(); + layout->addWidget(toggle3dButton); + layout->addStretch(); + commandLayout->addLayout(layout); + + QPushButton *toggleDeltaButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Toggle_Delta"), + QObject::tr("Toggle Delta"), this); + QObject::connect(toggleDeltaButton, SIGNAL(clicked(bool)), this, SLOT(toggleDeltaSlot(bool))); + layout = new QHBoxLayout(); + layout->addWidget(toggleDeltaButton); + layout->addStretch(); + commandLayout->addLayout(layout); + + QPushButton *clearAllButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Clear_All"), + QObject::tr("Clear All"), this); + QObject::connect(clearAllButton, SIGNAL(clicked(bool)), this, SLOT(clearAllSlot(bool))); + layout = new QHBoxLayout(); + layout->addWidget(clearAllButton); + layout->addStretch(); + commandLayout->addLayout(layout); +} + +void PartGui::DimensionControl::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::DimensionControl::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::DimensionControl::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +PartGui::TaskMeasureAngular::TaskMeasureAngular(): selections1(), selections2(), buttonSelectedIndex(0) +{ + setUpGui(); +} + +PartGui::TaskMeasureAngular::~TaskMeasureAngular() +{ + Gui::Selection().clearSelection(); +} + +void PartGui::TaskMeasureAngular::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + TopoDS_Shape shape; + if (!getShapeFromStrings(shape, std::string(msg.pDocName), std::string(msg.pObjectName), std::string(msg.pSubName))) + return; + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + gp_Vec pickPoint(msg.x, msg.y, msg.z); + if (buttonSelectedIndex == 0) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + if (shape.ShapeType() == TopAbs_VERTEX) + { + //if we have previous selection it should be only one vertex. + if (selections1.selections.size() > 1) + selections1.selections.clear(); + else if(selections1.selections.size() == 1) + { + //make sure it is a vertex. + if (selections1.selections.at(0).shapeType != DimSelections::Vertex) + selections1.selections.clear(); + } + + newSelection.shapeType = DimSelections::Vertex; + selections1.selections.push_back(newSelection); + if (selections1.selections.size() == 1) + return; + //here we should have 2 vertices, but will check to make sure. + assert(selections1.selections.size() == 2); + assert(selections1.selections.at(0).shapeType == DimSelections::Vertex); + assert(selections1.selections.at(1).shapeType == DimSelections::Vertex); + + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + + //here there should only be one in the selections container. so just clear it. + selections1.selections.clear(); + + if (shape.ShapeType() == TopAbs_EDGE) + { + newSelection.shapeType = DimSelections::Edge; + selections1.selections. push_back(newSelection); + } + + if (shape.ShapeType() == TopAbs_FACE) + { + newSelection.shapeType = DimSelections::Face; + selections1.selections.push_back(newSelection); + } + + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + } + if (buttonSelectedIndex == 1) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + if (shape.ShapeType() == TopAbs_VERTEX) + { + //if we have previous selection it should be only one vertex. + if (selections2.selections.size() > 1) + selections2.selections.clear(); + else if(selections2.selections.size() == 1) + { + //make sure it is a vertex. + if (selections2.selections.at(0).shapeType != DimSelections::Vertex) + selections2.selections.clear(); + } + + newSelection.shapeType = DimSelections::Vertex; + selections2.selections.push_back(newSelection); + if (selections2.selections.size() == 1) + return; + //here we should have 2 vertices, but will check to make sure. + assert(selections2.selections.size() == 2); + assert(selections2.selections.at(0).shapeType == DimSelections::Vertex); + assert(selections2.selections.at(1).shapeType == DimSelections::Vertex); + + buildDimension(); + clearSelection(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + //vertices have to be selected in succession. if we get here,clear temp selection. + selections2.selections.clear(); + + if (shape.ShapeType() == TopAbs_EDGE) + { + newSelection.shapeType = DimSelections::Edge; + selections2.selections. push_back(newSelection); + } + + if (shape.ShapeType() == TopAbs_FACE) + { + newSelection.shapeType = DimSelections::Face; + selections2.selections.push_back(newSelection); + } + + buildDimension(); + clearSelection(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + } +} + +void PartGui::TaskMeasureAngular::selectionClearDelayedSlot() +{ + //hack. + //clearing selections are not working as I hoped. Apparently the observer callback gets called + //before the actual selection takes place. Resulting in selections being left. this addresses this + //by being called from the event loop. + this->blockConnection(true); + Gui::Selection().clearSelection(); + this->blockConnection(false); +} + +PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui::DimSelections& selection) const +{ + assert(selection.selections.size() > 0 && selection.selections.size() < 3); + if (selection.selections.size() == 1) + { + DimSelections::DimSelection current = selection.selections.at(0); + if (current.shapeType == DimSelections::Edge) + { + TopoDS_Shape edgeShape; + if (!getShapeFromStrings(edgeShape, current.documentName, current.objectName, current.subObjectName)) + return VectorAdapter(); + TopoDS_Edge edge = TopoDS::Edge(edgeShape); + // make edge orientation so that end of edge closest to pick is head of vector. + gp_Vec firstPoint = PartGui::convert(TopExp::FirstVertex(edge, Standard_True)); + gp_Vec lastPoint = PartGui::convert(TopExp::LastVertex(edge, Standard_True)); + gp_Vec pickPoint(current.x, current.y, current.z); + double firstDistance = (firstPoint - pickPoint).Magnitude(); + double lastDistance = (lastPoint - pickPoint).Magnitude(); + if (lastDistance > firstDistance) + { + if (edge.Orientation() == TopAbs_FORWARD) + edge.Orientation(TopAbs_REVERSED); + else + edge.Orientation(TopAbs_FORWARD); + } + return VectorAdapter(edge, pickPoint); + } + if (current.shapeType == DimSelections::Face) + { + TopoDS_Shape faceShape; + if (!getShapeFromStrings(faceShape, current.documentName, current.objectName, current.subObjectName)) + return VectorAdapter(); + + TopoDS_Face face = TopoDS::Face(faceShape); + gp_Vec pickPoint(current.x, current.y, current.z); + return VectorAdapter(face, pickPoint); + } + } + //selection size == 2. + DimSelections::DimSelection current1 = selection.selections.at(0); + DimSelections::DimSelection current2 = selection.selections.at(1); + assert(current1.shapeType == DimSelections::Vertex); + assert(current2.shapeType == DimSelections::Vertex); + TopoDS_Shape vertexShape1, vertexShape2; + if(!getShapeFromStrings(vertexShape1, current1.documentName, current1.objectName, current1.subObjectName)) + return VectorAdapter(); + if(!getShapeFromStrings(vertexShape2, current2.documentName, current2.objectName, current2.subObjectName)) + return VectorAdapter(); + + TopoDS_Vertex vertex1 = TopoDS::Vertex(vertexShape1); + TopoDS_Vertex vertex2 = TopoDS::Vertex(vertexShape2); + + //build a temp adapter to make sure it is valid. + return VectorAdapter(PartGui::convert(vertex2), PartGui::convert(vertex1)); +} + +void PartGui::TaskMeasureAngular::buildDimension() +{ + //build adapters. + VectorAdapter adapt1 = buildAdapter(selections1); + VectorAdapter adapt2 = buildAdapter(selections2); + + if (!adapt1.isValid() || !adapt2.isValid()) + { + Base::Console().Message("\ncouldn't build adapter\n\n"); + return; + } + goDimensionAngularNoTask(adapt1, adapt2); +} + +void PartGui::TaskMeasureAngular::clearSelection() +{ + selections1.selections.clear(); + selections2.selections.clear(); +} + +void PartGui::TaskMeasureAngular::setUpGui() +{ + QPixmap mainIcon = Gui::BitmapFactory().pixmap("Part_Measure_Angular"); + + Gui::TaskView::TaskBox* selectionTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Selections"), false, 0); + QVBoxLayout *selectionLayout = new QVBoxLayout(); + stepped = new SteppedSelection(2, selectionTaskBox); + selectionLayout->addWidget(stepped); + selectionTaskBox->groupLayout()->addLayout(selectionLayout); + + Gui::TaskView::TaskBox* controlTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Control"), false, 0); + QVBoxLayout *controlLayout = new QVBoxLayout(); + + QHBoxLayout *resetLayout = new QHBoxLayout(); + resetLayout->addSpacing(5); //hack for alignment. + QPushButton *resetButton = new QPushButton(mainIcon, QObject::tr("Reset Dialog"), controlTaskBox); + resetLayout->addWidget(resetButton); + resetLayout->addStretch(); + controlLayout->addLayout(resetLayout); + QObject::connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); + + DimensionControl *control = new DimensionControl(controlTaskBox); + controlLayout->addWidget(control); + controlTaskBox->groupLayout()->addLayout(controlLayout); + + this->setButtonPosition(TaskDialog::South); + Content.push_back(selectionTaskBox); + Content.push_back(controlTaskBox); + + stepped->getButton(0)->setChecked(true);//before wired up. + QObject::connect(stepped->getButton(0), SIGNAL(toggled(bool)), this, SLOT(selection1Slot(bool))); + QObject::connect(stepped->getButton(1), SIGNAL(toggled(bool)), this, SLOT(selection2Slot(bool))); +} + +void PartGui::TaskMeasureAngular::selection1Slot(bool checked) +{ + if (checked) + { + buttonSelectedIndex = 0; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections1.selections.begin(); it != selections1.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); + } + else + { + if (selections1.selections.size() > 0) + stepped->setIconDone(0); + } +} + +void PartGui::TaskMeasureAngular::selection2Slot(bool checked) +{ + if (checked) + buttonSelectedIndex = 1; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections2.selections.begin(); it != selections2.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureAngular::resetDialogSlot(bool) +{ + clearSelection(); + this->blockConnection(true); + Gui::Selection().clearSelection(); + stepped->getButton(0)->setChecked(true); + this->blockConnection(false); +} + +void PartGui::TaskMeasureAngular::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::TaskMeasureAngular::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::TaskMeasureAngular::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +#include "moc_TaskDimension.cpp" diff --git a/src/Mod/Part/Gui/TaskDimension.h b/src/Mod/Part/Gui/TaskDimension.h new file mode 100644 index 0000000000..de09b90c42 --- /dev/null +++ b/src/Mod/Part/Gui/TaskDimension.h @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (c) 2013 Thomas Anderson * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TASKDIMENSION_H +#define TASKDIMENSION_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class TopoDS_Shape; +class TopoDS_Face; +class TopoDS_Edge; +class TopoDS_Vertex; +class gp_Pnt; +class BRepExtrema_DistShapeShape; + +class QPushButton; +class QPixmap; +class QLabel; + +namespace Gui{class View3dInventorViewer;} + +namespace PartGui +{ + /*!find shape from selection strings + * @param shapeOut search results. + * @param doc document name to search. + * @param object object name to search. + * @param sub sub-object name to search. + * @return signal if the search was successful. + */ + bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub); + /*!examine pre selection + * @param shape1 firt shape in current selection + * @param shape2 second shape in current selection + * @return signal if preselection is valid. false means shape1 and shape2 are invalid. + */ + bool evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2); + /*!start of the measure linear command*/ + void goDimensionLinearRoot(); + /*!does the measure and create dimensions without a dialog + * @param shape1 first shape. + * @param shape2 second shape. + * @todo incorporate some form of "adapt to topods_shape". so we can expand to other types outside OCC. + */ + void goDimensionLinearNoTask(const TopoDS_Shape &shape1, const TopoDS_Shape &shape2); + /*!prints results of measuring to console. + * @param measure object containing the measure information + */ + void dumpLinearResults(const BRepExtrema_DistShapeShape &measure); + /*!convenience function to get the viewer*/ + Gui::View3DInventorViewer* getViewer(); + /*!adds 3d and delta dimensions to the viewer + * @param measure object containing the measure information. + */ + void addLinearDimensions(const BRepExtrema_DistShapeShape &measure); + /*!creates one dimension from points with color + * @param point1 first point + * @param point2 second point + * @param color color of dimension + * @return an inventor node to add to a scenegraph + */ + SoNode* createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color); + /*!erases all the dimensions in the viewer.*/ + void eraseAllDimensions(); + /*!toggles the display status of the 3d dimensions*/ + void toggle3d(); + /*!toggles the display status of the delta dimensions*/ + void toggleDelta(); + /*!make sure measure command isn't working with everthing invisible. Confusing the user*/ + void ensureSomeDimensionVisible(); + /*!make sure angle measure command isn't working with 3d off. Confusing the user*/ + void ensure3dDimensionVisible(); + /*convert a vertex to vector*/ + gp_Vec convert(const TopoDS_Vertex &vertex); + +class DimensionLinear : public SoSeparatorKit +{ + SO_KIT_HEADER(DimensionLinear); + + SO_KIT_CATALOG_ENTRY_HEADER(transformation); + SO_KIT_CATALOG_ENTRY_HEADER(annotate); + SO_KIT_CATALOG_ENTRY_HEADER(leftArrow); + SO_KIT_CATALOG_ENTRY_HEADER(rightArrow); + SO_KIT_CATALOG_ENTRY_HEADER(line); + SO_KIT_CATALOG_ENTRY_HEADER(textSep); +public: + DimensionLinear(); + static void initClass(); + virtual SbBool affectsState() const; + + SoSFVec3f point1; + SoSFVec3f point2; + SoSFString text; + SoSFColor dColor; +protected: + SoSFRotation rotate; + SoSFFloat length; + SoSFVec3f origin; + +private: + virtual ~DimensionLinear(); + void setupDimension(); +}; + +/*kit for anglular dimensions*/ +class DimensionAngular : public SoSeparatorKit +{ + SO_KIT_HEADER(DimensionAngular); + + SO_KIT_CATALOG_ENTRY_HEADER(transformation); + SO_KIT_CATALOG_ENTRY_HEADER(annotate); + SO_KIT_CATALOG_ENTRY_HEADER(arrow1); + SO_KIT_CATALOG_ENTRY_HEADER(arrow2); + SO_KIT_CATALOG_ENTRY_HEADER(arcSep); + SO_KIT_CATALOG_ENTRY_HEADER(textSep); +public: + DimensionAngular(); + static void initClass(); + virtual SbBool affectsState() const; + + SoSFFloat radius;//radians. + SoSFFloat angle;//radians. + SoSFString text; + SoSFColor dColor; + SoSFMatrix matrix; +private: + virtual ~DimensionAngular(); + void setupDimension(); +}; + +/*used for generating points for arc display*/ +class ArcEngine : public SoEngine +{ + SO_ENGINE_HEADER(ArcEngine); +public: + ArcEngine(); + static void initClass(); + + SoSFFloat radius; + SoSFFloat angle; + SoSFFloat deviation; + + SoEngineOutput points; + SoEngineOutput pointCount; +protected: + virtual void evaluate(); +private: + virtual ~ArcEngine(){} + void defaultValues(); //some non error values if something goes wrong. +}; + +/*! a widget with buttons and icons for a controlled selection process*/ +class SteppedSelection : public QWidget +{ + Q_OBJECT +public: + SteppedSelection(const uint &buttonCountIn, QWidget *parent = 0); + ~SteppedSelection(); + QPushButton* getButton(const uint &index); + void setIconDone(const uint &index); + +protected: + typedef std::pair ButtonIconPairType; + std::vector buttons; + QPixmap *stepActive; + QPixmap *stepDone; + +private slots: + void selectionSlot(bool checked); + void buildPixmaps(); + +}; + +/*! just convenience container*/ +class DimSelections +{ +public: + enum ShapeType{None, Vertex, Edge, Face}; + struct DimSelection + { + std::string documentName; + std::string objectName; + std::string subObjectName; + float x; + float y; + float z; + ShapeType shapeType; + }; + std::vector selections; +}; + +/*!widget for buttons controlling the display of dimensions*/ +class DimensionControl : public QWidget +{ + Q_OBJECT +public: + explicit DimensionControl(QWidget* parent); +public slots: + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); +}; + +/*!linear dialog*/ +class TaskMeasureLinear : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver +{ + Q_OBJECT +public: + TaskMeasureLinear(); + ~TaskMeasureLinear(); + + virtual QDialogButtonBox::StandardButtons getStandardButtons() const + {return QDialogButtonBox::Close;} + virtual bool isAllowedAlterDocument(void) const {return false;} + virtual bool needsFullSpace() const {return false;} +protected: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +protected slots: + void selection1Slot(bool checked); + void selection2Slot(bool checked); + void resetDialogSlot(bool); + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); + void selectionClearDelayedSlot(); + +private: + void setUpGui(); + void buildDimension(); + void clearSelectionStrings(); + DimSelections selections1; + DimSelections selections2; + uint buttonSelectedIndex; + SteppedSelection *stepped; + +}; + +/*! @brief Convert to vector + * + * Used to construct a vector from various input types + */ +class VectorAdapter +{ +public: + /*!default construction isValid is set to false*/ + VectorAdapter(); + /*!Build a vector from a faceIn + * @param faceIn vector will be normal to plane and equal to cylindrical axis. + * @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/ + VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn); + /*!Build a vector from an edgeIn + * @param edgeIn vector will be lastPoint - firstPoint. + * @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/ + VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn); + /*!Build a vector From 2 vertices. + *vector will be equal to @param vertex2In - @param vertex1In.*/ + VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In); + /*!Build a vector From 2 vectors. + *vector will be equal to @param vector2 - @param vector1.*/ + VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2); + + /*!make sure no errors in vector construction. + * @return true = vector is good. false = vector is NOT good.*/ + bool isValid() const {return status;} + /*!get the calculated vector. + * @return the vector. use isValid to ensure correct results.*/ + operator gp_Vec() const {return vector;} + /*!build occ line used for extrema calculation*/ + operator gp_Lin() const; + gp_Vec getPickPoint() const {return origin;} + +private: + void projectOriginOntoVector(const gp_Vec &pickedPointIn); + bool status; + gp_Vec vector; + gp_Vec origin; +}; + +/*!angular dialog class*/ +class TaskMeasureAngular : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver +{ + Q_OBJECT +public: + TaskMeasureAngular(); + ~TaskMeasureAngular(); + + virtual QDialogButtonBox::StandardButtons getStandardButtons() const + {return QDialogButtonBox::Close;} + virtual bool isAllowedAlterDocument(void) const {return false;} + virtual bool needsFullSpace() const {return false;} +protected: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +protected slots: + void selection1Slot(bool checked); + void selection2Slot(bool checked); + void resetDialogSlot(bool); + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); + void selectionClearDelayedSlot(); + +private: + void setUpGui(); + void buildDimension(); + void clearSelection(); + DimSelections selections1; + DimSelections selections2; + uint buttonSelectedIndex; + SteppedSelection *stepped; + VectorAdapter buildAdapter(const DimSelections &selection) const; +}; + +/*!start of the measure angular command*/ +void goDimensionAngularRoot(); +/*!examine angular pre selection + * @param vector1Out firt shape in current selection + * @param vector2Out second shape in current selection + * @return signal if preselection is valid. false means vector1Out and vector2Out are invalid. + */ +bool evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out); +/*!build angular dimension*/ +void goDimensionAngularNoTask(const VectorAdapter &vector1Adapter, const VectorAdapter &vector2Adapter); +} + +#endif // TASKDIMENSION_H