/*************************************************************************** * 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 "PreCompiled.h" #ifndef _PreCpmp_ # include # include #endif #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 #include #include #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; gp_Pnt point1 = measure.PointOnShape1(1); gp_Pnt point2 = measure.PointOnShape2(1); 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)->setEnabled(true); 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); stepped->getButton(1)->setEnabled(false); 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(); DimensionControl *control = new DimensionControl(controlTaskBox); controlLayout->addWidget(control); controlTaskBox->groupLayout()->addLayout(controlLayout); QObject::connect(control->resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); this->setButtonPosition(TaskDialog::South); Content.push_back(selectionTaskBox); Content.push_back(controlTaskBox); stepped->getButton(0)->setChecked(true);//before wired up. stepped->getButton(0)->setEnabled(true); 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); stepped->getButton(1)->setEnabled(false); 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.0) stream << std::setprecision(std::numeric_limits::digits10 + 1) << "complement in radians is: " << M_PI / 2.0 - 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.0 && 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.0; 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.0, 0.0, 1.0), Precision::Angular())) zAxis = gp_Vec(0.0, 0.0, 1.0); else zAxis = gp_Vec(0.0, 1.0, 0.0); } 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.0); 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.1f); 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.07f, 7.07f, 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) , stepActive(0) , stepDone(0) { 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); button->setEnabled(false); 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); resetButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Linear"), QObject::tr("Reset Dialog"), this); commandLayout->addWidget(resetButton); 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))); commandLayout->addWidget(toggle3dButton); 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))); commandLayout->addWidget(toggleDeltaButton); 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))); commandLayout->addWidget(clearAllButton); } 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)->setEnabled(true); 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)->setEnabled(true); 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); stepped->getButton(1)->setEnabled(false); 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); stepped->getButton(1)->setEnabled(false); 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(); DimensionControl *control = new DimensionControl(controlTaskBox); controlLayout->addWidget(control); controlTaskBox->groupLayout()->addLayout(controlLayout); QObject::connect(control->resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); this->setButtonPosition(TaskDialog::South); Content.push_back(selectionTaskBox); Content.push_back(controlTaskBox); stepped->getButton(0)->setChecked(true);//before wired up. stepped->getButton(0)->setEnabled(true); 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); stepped->getButton(1)->setEnabled(false); 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"