/*************************************************************************** * Copyright (c) 2013 Jan Rheinlaender * * * * 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 _PreComp_ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #endif #include "ViewProviderDatum.h" #include "TaskDatumParameters.h" #include "Workbench.h" #include #include #include #include using namespace PartDesignGui; PROPERTY_SOURCE(PartDesignGui::ViewProviderDatum,Gui::ViewProviderGeometryObject) ViewProviderDatum::ViewProviderDatum() { pShapeSep = new SoSeparator(); pShapeSep->ref(); } ViewProviderDatum::~ViewProviderDatum() { pShapeSep->unref(); } void ViewProviderDatum::attach(App::DocumentObject *obj) { ViewProviderDocumentObject::attach(obj); PartDesign::Datum* pcDatum = static_cast(getObject()); if (pcDatum->getTypeId() == PartDesign::Plane::getClassTypeId()) datumType = QObject::tr("Plane"); else if (pcDatum->getTypeId() == PartDesign::Line::getClassTypeId()) datumType = QObject::tr("Line"); else if (pcDatum->getTypeId() == PartDesign::Point::getClassTypeId()) datumType = QObject::tr("Point"); SoSeparator* sep = new SoSeparator(); SoPickStyle* ps = new SoPickStyle(); ps->style = SoPickStyle::SHAPE; SoShapeHints* hints = new SoShapeHints(); hints->shapeType.setValue(SoShapeHints::UNKNOWN_SHAPE_TYPE); hints->vertexOrdering.setValue(SoShapeHints::COUNTERCLOCKWISE); SoBaseColor* color = new SoBaseColor(); color->rgb.setValue(0.9, 0.9, 0.1); SoMaterial* material = new SoMaterial(); material->diffuseColor.setValue(0.9f, 0.9f, 0.1f); material->transparency.setValue(0.2); sep->addChild(hints); sep->addChild(color); sep->addChild(material); sep->addChild(pShapeSep); addDisplayMaskMode(sep, "Base"); } std::vector ViewProviderDatum::getDisplayModes(void) const { // add modes std::vector StrList; StrList.push_back("Base"); return StrList; } void ViewProviderDatum::setDisplayMode(const char* ModeName) { if (strcmp(ModeName, "Base") == 0) setDisplayMaskMode("Base"); ViewProviderDocumentObject::setDisplayMode(ModeName); } void ViewProviderDatum::onChanged(const App::Property* prop) { /*if (prop == &Shape) { updateData(prop); } else {*/ ViewProviderDocumentObject::onChanged(prop); //} } void ViewProviderDatum::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QAction* act; act = menu->addAction(QObject::tr("Edit datum ") + datumType, receiver, member); act->setData(QVariant((int)ViewProvider::Default)); Gui::ViewProviderGeometryObject::setupContextMenu(menu, receiver, member); } bool ViewProviderDatum::setEdit(int ModNum) { if (!ViewProvider::setEdit(ModNum)) return false; if (ModNum == ViewProvider::Default ) { // When double-clicking on the item for this datum feature the // object unsets and sets its edit mode without closing // the task panel Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); TaskDlgDatumParameters *datumDlg = qobject_cast(dlg); if (datumDlg && datumDlg->getDatumView() != this) datumDlg = 0; // another datum feature left open its task panel if (dlg && !datumDlg) { QMessageBox msgBox; msgBox.setText(QObject::tr("A dialog is already open in the task panel")); msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if (ret == QMessageBox::Yes) Gui::Control().closeDialog(); else return false; } // clear the selection (convenience) Gui::Selection().clearSelection(); // always change to PartDesign WB, remember where we come from oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench"); // start the edit dialog if (datumDlg) Gui::Control().showDialog(datumDlg); else Gui::Control().showDialog(new TaskDlgDatumParameters(this)); return true; } else { return ViewProvider::setEdit(ModNum); } } void ViewProviderDatum::unsetEdit(int ModNum) { // return to the WB we were in before editing the PartDesign feature Gui::Command::assureWorkbench(oldWb.c_str()); if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog Gui::Control().closeDialog(); } else { Gui::ViewProviderGeometryObject::unsetEdit(ModNum); } } PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPoint,PartDesignGui::ViewProviderDatum) ViewProviderDatumPoint::ViewProviderDatumPoint() { SoMarkerSet* points = new SoMarkerSet(); points->markerIndex = SoMarkerSet::DIAMOND_FILLED_9_9; points->numPoints = 0; pShapeSep->addChild(points); } ViewProviderDatumPoint::~ViewProviderDatumPoint() { } void ViewProviderDatumPoint::updateData(const App::Property* prop) { // Gets called whenever a property of the attached object changes PartDesign::Point* pcDatum = static_cast(this->getObject()); if (strcmp(prop->getName(),"_Point") == 0) { Base::Vector3d p = pcDatum->_Point.getValue(); SoMFVec3f v; v.setNum(1); v.set1Value(0, p.x, p.y, p.z); SoMarkerSet* points = static_cast(pShapeSep->getChild(0)); SoVertexProperty* vprop; if (points->vertexProperty.getValue() == NULL) { vprop = new SoVertexProperty(); vprop->vertex = v; points->vertexProperty = vprop; } else { vprop = static_cast(points->vertexProperty.getValue()); vprop->vertex = v; } points->numPoints = 1; } ViewProviderDatum::updateData(prop); } PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumLine,PartDesignGui::ViewProviderDatum) ViewProviderDatumLine::ViewProviderDatumLine() { SoLineSet* lineSet = new SoLineSet(); pShapeSep->addChild(lineSet); } ViewProviderDatumLine::~ViewProviderDatumLine() { } void ViewProviderDatumLine::updateData(const App::Property* prop) { // Gets called whenever a property of the attached object changes PartDesign::Line* pcDatum = static_cast(this->getObject()); if (strcmp(prop->getName(),"_Base") == 0) { Base::Vector3d base = pcDatum->_Base.getValue(); Base::Vector3d dir = pcDatum->_Direction.getValue(); // Get limits of the line from bounding box of the body PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); if (body == NULL) return; Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); if (tipSolid == NULL) return; Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); Base::Vector3d p1, p2; if (bbox.IsInBox(base)) { bbox.IntersectionPoint(base, dir, p1, Precision::Confusion()); bbox.IntersectionPoint(base, -dir, p2, Precision::Confusion()); } else { bbox.IntersectWithLine(base, dir, p1, p2); if ((p1 == Base::Vector3d(0,0,0)) && (p2 == Base::Vector3d(0,0,0))) bbox.IntersectWithLine(base, -dir, p1, p2); } // Display the line SoMFVec3f v; v.setNum(2); v.set1Value(0, p1.x, p1.y, p1.z); v.set1Value(1, p2.x, p2.y, p2.z); SoLineSet* lineSet = static_cast(pShapeSep->getChild(0)); SoVertexProperty* vprop; if (lineSet->vertexProperty.getValue() == NULL) { vprop = new SoVertexProperty(); vprop->vertex = v; lineSet->vertexProperty = vprop; } else { vprop = static_cast(lineSet->vertexProperty.getValue()); vprop->vertex = v; } SoMFInt32 idx; idx.setNum(1); idx.set1Value(0, 2); lineSet->numVertices = idx; } ViewProviderDatum::updateData(prop); } PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPlane,PartDesignGui::ViewProviderDatum) ViewProviderDatumPlane::ViewProviderDatumPlane() { SoFaceSet* faceSet = new SoFaceSet(); pShapeSep->addChild(faceSet); } ViewProviderDatumPlane::~ViewProviderDatumPlane() { } void ViewProviderDatumPlane::updateData(const App::Property* prop) { // Gets called whenever a property of the attached object changes PartDesign::Plane* pcDatum = static_cast(this->getObject()); if (strcmp(prop->getName(),"_Base") == 0) { Base::Vector3d base = pcDatum->_Base.getValue(); Base::Vector3d normal = pcDatum->_Normal.getValue(); // Get limits of the plane from bounding box of the body PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); if (body == NULL) return; Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); if (tipSolid == NULL) return; Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); // Calculate intersection of plane with bounding box edges // TODO: This can be a lot more efficient if we do the maths ourselves, e.g. // http://cococubed.asu.edu/code_pages/raybox.shtml // http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf Handle_Geom_Plane plane = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); std::vector points; for (int i = 0; i < 12; i++) { // Get the edge of the bounding box Base::Vector3d p1, p2; bbox.CalcDistance(i, p1, p2); Base::Vector3d ldir = p2 - p1; Handle_Geom_Line line = new Geom_Line(gp_Pnt(p1.x, p1.y, p1.z), gp_Dir(ldir.x, ldir.y, ldir.z)); GeomAPI_IntCS intersector(line, plane); if (!intersector.IsDone() || (intersector.NbPoints() == 0)) continue; gp_Pnt pnt = intersector.Point(1); Base::Vector3d point(pnt.X(), pnt.Y(), pnt.Z()); // Check whether intersection is on the bbox edge (bbox.IsInside() always tests false) double edgeLength = (p1 - p2).Length(); double l1 = (p1 - point).Length(); double l2 = (p2 - point).Length(); if (fabs(edgeLength - l1 - l2) > 0.001) continue; // Check for duplicates bool duplicate = false; for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { if ((point - *p).Sqr() < Precision::Confusion()) { duplicate = true; break; } } if (!duplicate) points.push_back(point); } if (points.size() < 3) return; // Sort the points to get a proper polygon, see http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf p.5 if (points.size() > 3) { // Longest component of normal vector int longest; if (normal.x > normal.y) if (normal.x > normal.z) longest = 0; // x is longest else longest = 2; // z is longest else if (normal.y > normal.z) longest = 1; // y is longest else longest = 2; // z is longest // mean value for intersection points Base::Vector3d m; for (std::vector::iterator p = points.begin(); p != points.end(); p++) m += *p; m /= points.size(); // Sort by angles double a[points.size()]; for (int i = 0; i < points.size() - 1; i++) { if (longest == 0) a[i] = atan2(points[i].z - m.z, points[i].y - m.y); else if (longest == 1) a[i] = atan2(points[i].z - m.z, points[i].x - m.x); else a[i] = atan2(points[i].y - m.y, points[i].x - m.x); for (int k = i+1; k < points.size(); k++) { if (longest == 0) a[k] = atan2(points[k].z - m.z, points[k].y - m.y); else if (longest == 1) a[k] = atan2(points[k].z - m.z, points[k].x - m.x); else a[k] = atan2(points[k].y - m.y, points[k].x - m.x); if (a[k] < a[i]) { Base::Vector3d temp = points[i]; points[i] = points[k]; points[k] = temp; a[i] = a[k]; } } } } // Display the plane SoMFVec3f v; v.setNum(points.size()); for (int p = 0; p < points.size(); p++) v.set1Value(p, points[p].x, points[p].y, points[p].z); SoFaceSet* faceSet = static_cast(pShapeSep->getChild(0)); SoVertexProperty* vprop; if (faceSet->vertexProperty.getValue() == NULL) { vprop = new SoVertexProperty(); vprop->vertex = v; faceSet->vertexProperty = vprop; } else { vprop = static_cast(faceSet->vertexProperty.getValue()); vprop->vertex = v; } SoMFInt32 idx; idx.setNum(1); idx.set1Value(0, points.size()); faceSet->numVertices = idx; } ViewProviderDatum::updateData(prop); }