cross-section of meshes
This commit is contained in:
@@ -1051,6 +1051,31 @@ bool CmdMeshSectionByPlane::isActive(void)
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
DEF_STD_CMD_A(CmdMeshCrossSections)
|
||||
|
||||
CmdMeshCrossSections::CmdMeshCrossSections()
|
||||
: Command("Mesh_CrossSections")
|
||||
{
|
||||
sAppModule = "Mesh";
|
||||
sGroup = QT_TR_NOOP("Mesh");
|
||||
sMenuText = QT_TR_NOOP("Cross-sections...");
|
||||
sToolTipText = QT_TR_NOOP("Cross-sections");
|
||||
sStatusTip = QT_TR_NOOP("Cross-sections");
|
||||
}
|
||||
|
||||
void CmdMeshCrossSections::activated(int)
|
||||
{
|
||||
doCommand(Doc,"import MeshPartGui, FreeCADGui\nFreeCADGui.runCommand('MeshPart_CrossSections')\n");
|
||||
}
|
||||
|
||||
bool CmdMeshCrossSections::isActive(void)
|
||||
{
|
||||
return (Gui::Selection().countObjectsOfType(Mesh::Feature::getClassTypeId()) > 0 &&
|
||||
!Gui::Control().activeDialog());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
DEF_STD_CMD_A(CmdMeshPolySplit)
|
||||
|
||||
CmdMeshPolySplit::CmdMeshPolySplit()
|
||||
@@ -1769,6 +1794,7 @@ void CreateMeshCommands(void)
|
||||
rcCmdMgr.addCommand(new CmdMeshPolyTrim());
|
||||
rcCmdMgr.addCommand(new CmdMeshTrimByPlane());
|
||||
rcCmdMgr.addCommand(new CmdMeshSectionByPlane());
|
||||
rcCmdMgr.addCommand(new CmdMeshCrossSections());
|
||||
rcCmdMgr.addCommand(new CmdMeshEvaluation());
|
||||
rcCmdMgr.addCommand(new CmdMeshEvaluateFacet());
|
||||
rcCmdMgr.addCommand(new CmdMeshEvaluateSolid());
|
||||
|
||||
@@ -195,7 +195,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
<< "Mesh_PolyTrim"
|
||||
//<< "Mesh_PolySegm"
|
||||
<< "Mesh_TrimByPlane"
|
||||
<< "Mesh_SectionByPlane";
|
||||
<< "Mesh_SectionByPlane"
|
||||
<< "Mesh_CrossSections";
|
||||
|
||||
mesh->setCommand("&Meshes");
|
||||
*mesh << "Mesh_Import"
|
||||
|
||||
@@ -35,6 +35,7 @@ set(MeshPartGui_LIBS
|
||||
)
|
||||
|
||||
set(MeshPartGui_MOC_HDRS
|
||||
CrossSections.h
|
||||
CurveOnMesh.h
|
||||
TaskCurveOnMesh.h
|
||||
Tessellation.h
|
||||
@@ -43,6 +44,7 @@ fc_wrap_cpp(MeshPartGui_MOC_SRCS ${MeshPartGui_MOC_HDRS})
|
||||
SOURCE_GROUP("Moc" FILES ${MeshPartGui_MOC_SRCS})
|
||||
|
||||
set(MeshPartGui_UIC_SRCS
|
||||
CrossSections.ui
|
||||
TaskCurveOnMesh.ui
|
||||
Tessellation.ui
|
||||
)
|
||||
@@ -60,6 +62,9 @@ SET(MeshPartGui_SRCS
|
||||
${MeshPartGui_UIC_HDRS}
|
||||
AppMeshPartGui.cpp
|
||||
Command.cpp
|
||||
CrossSections.ui
|
||||
CrossSections.cpp
|
||||
CrossSections.h
|
||||
CurveOnMesh.cpp
|
||||
CurveOnMesh.h
|
||||
Resources/MeshPart.qrc
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <Gui/View3DInventor.h>
|
||||
#include <Gui/View3DInventorViewer.h>
|
||||
#include "Tessellation.h"
|
||||
#include "CrossSections.h"
|
||||
#include "TaskCurveOnMesh.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -263,6 +264,45 @@ bool CmdMeshPartSection::isActive(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// MeshPart_CrossSections
|
||||
//===========================================================================
|
||||
DEF_STD_CMD_A(CmdMeshPartCrossSections)
|
||||
|
||||
CmdMeshPartCrossSections::CmdMeshPartCrossSections()
|
||||
:Command("MeshPart_CrossSections")
|
||||
{
|
||||
sAppModule = "MeshPart";
|
||||
sGroup = QT_TR_NOOP("MeshPart");
|
||||
sMenuText = QT_TR_NOOP("Cross-sections...");
|
||||
sToolTipText = QT_TR_NOOP("Cross-sections");
|
||||
sWhatsThis = "MeshPart_CrossSections";
|
||||
sStatusTip = sToolTipText;
|
||||
//sPixmap = "MeshPart_CrossSections";
|
||||
}
|
||||
|
||||
void CmdMeshPartCrossSections::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
|
||||
if (!dlg) {
|
||||
std::vector<App::DocumentObject*> obj = Gui::Selection().getObjectsOfType
|
||||
(Mesh::Feature::getClassTypeId());
|
||||
Base::BoundBox3d bbox;
|
||||
for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
|
||||
bbox.Add(static_cast<Mesh::Feature*>(*it)->Mesh.getBoundingBox());
|
||||
}
|
||||
dlg = new MeshPartGui::TaskCrossSections(bbox);
|
||||
}
|
||||
Gui::Control().showDialog(dlg);
|
||||
}
|
||||
|
||||
bool CmdMeshPartCrossSections::isActive(void)
|
||||
{
|
||||
return (Gui::Selection().countObjectsOfType(Mesh::Feature::getClassTypeId()) > 0 &&
|
||||
!Gui::Control().activeDialog());
|
||||
}
|
||||
|
||||
DEF_STD_CMD_A(CmdMeshPartCurveOnMesh)
|
||||
|
||||
CmdMeshPartCurveOnMesh::CmdMeshPartCurveOnMesh()
|
||||
@@ -307,5 +347,6 @@ void CreateMeshPartCommands(void)
|
||||
rcCmdMgr.addCommand(new CmdMeshPartMesher());
|
||||
rcCmdMgr.addCommand(new CmdMeshPartTrimByPlane());
|
||||
rcCmdMgr.addCommand(new CmdMeshPartSection());
|
||||
rcCmdMgr.addCommand(new CmdMeshPartCrossSections());
|
||||
rcCmdMgr.addCommand(new CmdMeshPartCurveOnMesh());
|
||||
}
|
||||
|
||||
611
src/Mod/MeshPart/Gui/CrossSections.cpp
Normal file
611
src/Mod/MeshPart/Gui/CrossSections.cpp
Normal file
@@ -0,0 +1,611 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2019 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* 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 <sstream>
|
||||
# include <Standard_math.hxx>
|
||||
# include <BRep_Builder.hxx>
|
||||
# include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <TopoDS_Compound.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
# include <cfloat>
|
||||
# include <QFuture>
|
||||
# include <QFutureWatcher>
|
||||
# include <QKeyEvent>
|
||||
# include <QMessageBox>
|
||||
# include <QtConcurrentMap>
|
||||
# include <boost/bind.hpp>
|
||||
# include <Python.h>
|
||||
# include <Inventor/nodes/SoBaseColor.h>
|
||||
# include <Inventor/nodes/SoCoordinate3.h>
|
||||
# include <Inventor/nodes/SoDrawStyle.h>
|
||||
# include <Inventor/nodes/SoFaceSet.h>
|
||||
# include <Inventor/nodes/SoLineSet.h>
|
||||
# include <Inventor/nodes/SoSeparator.h>
|
||||
#endif
|
||||
|
||||
#include "ui_CrossSections.h"
|
||||
#include "CrossSections.h"
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Part/App/Tools.h>
|
||||
#include <Mod/Mesh/App/MeshFeature.h>
|
||||
#include <Mod/Mesh/App/Core/Algorithm.h>
|
||||
#include <Mod/Mesh/App/Core/Grid.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/View3DInventor.h>
|
||||
#include <Gui/View3DInventorViewer.h>
|
||||
#include <Base/UnitsApi.h>
|
||||
|
||||
using namespace MeshPartGui;
|
||||
|
||||
namespace MeshPartGui {
|
||||
class ViewProviderCrossSections : public Gui::ViewProvider
|
||||
{
|
||||
public:
|
||||
ViewProviderCrossSections()
|
||||
{
|
||||
coords = new SoCoordinate3();
|
||||
coords->ref();
|
||||
planes = new SoLineSet();
|
||||
planes->ref();
|
||||
SoBaseColor* color = new SoBaseColor();
|
||||
color->rgb.setValue(1.0f, 0.447059f, 0.337255f);
|
||||
SoDrawStyle* style = new SoDrawStyle();
|
||||
style->lineWidth.setValue(2.0f);
|
||||
this->pcRoot->addChild(color);
|
||||
this->pcRoot->addChild(style);
|
||||
this->pcRoot->addChild(coords);
|
||||
this->pcRoot->addChild(planes);
|
||||
}
|
||||
~ViewProviderCrossSections()
|
||||
{
|
||||
coords->unref();
|
||||
planes->unref();
|
||||
}
|
||||
void updateData(const App::Property*)
|
||||
{
|
||||
}
|
||||
const char* getDefaultDisplayMode() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
std::vector<std::string> getDisplayModes(void) const
|
||||
{
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
void setCoords(const std::vector<Base::Vector3f>& v)
|
||||
{
|
||||
coords->point.setNum(v.size());
|
||||
SbVec3f* p = coords->point.startEditing();
|
||||
for (unsigned int i=0; i<v.size(); i++) {
|
||||
const Base::Vector3f& pt = v[i];
|
||||
p[i].setValue(pt.x,pt.y,pt.z);
|
||||
}
|
||||
coords->point.finishEditing();
|
||||
unsigned int count = v.size()/5;
|
||||
planes->numVertices.setNum(count);
|
||||
int32_t* l = planes->numVertices.startEditing();
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
l[i] = 5;
|
||||
}
|
||||
planes->numVertices.finishEditing();
|
||||
}
|
||||
|
||||
private:
|
||||
SoCoordinate3* coords;
|
||||
SoLineSet* planes;
|
||||
};
|
||||
|
||||
class MeshCrossSection {
|
||||
public:
|
||||
MeshCrossSection(const MeshCore::MeshKernel& mesh,
|
||||
const MeshCore::MeshFacetGrid& grid,
|
||||
double x, double y, double z,
|
||||
bool connectEdges, double eps)
|
||||
: mesh(mesh)
|
||||
, grid(grid)
|
||||
, x(x)
|
||||
, y(y)
|
||||
, z(z)
|
||||
, connectEdges(connectEdges)
|
||||
, epsilon(eps)
|
||||
{
|
||||
}
|
||||
std::list<TopoDS_Wire> section(double d)
|
||||
{
|
||||
Mesh::MeshObject::TPolylines polylines;
|
||||
MeshCore::MeshAlgorithm algo(mesh);
|
||||
Base::Vector3f p(x*d, y*d, z*d);
|
||||
Base::Vector3f n(x, y, z);
|
||||
algo.CutWithPlane(p, n, grid, polylines, epsilon, connectEdges);
|
||||
|
||||
std::list<TopoDS_Wire> wires;
|
||||
for (auto it = polylines.begin(); it != polylines.end(); ++it) {
|
||||
BRepBuilderAPI_MakePolygon mkPoly;
|
||||
for (auto jt = it->begin(); jt != it->end(); ++jt) {
|
||||
mkPoly.Add(Base::convertTo<gp_Pnt>(*jt));
|
||||
}
|
||||
|
||||
if (mkPoly.IsDone())
|
||||
wires.push_back(mkPoly.Wire());
|
||||
}
|
||||
|
||||
return wires;
|
||||
}
|
||||
|
||||
private:
|
||||
const MeshCore::MeshKernel& mesh;
|
||||
const MeshCore::MeshFacetGrid& grid;
|
||||
double x,y,z;
|
||||
bool connectEdges;
|
||||
double epsilon;
|
||||
};
|
||||
}
|
||||
|
||||
CrossSections::CrossSections(const Base::BoundBox3d& bb, QWidget* parent, Qt::WindowFlags fl)
|
||||
: QDialog(parent, fl), bbox(bb)
|
||||
{
|
||||
ui = new Ui_CrossSections();
|
||||
ui->setupUi(this);
|
||||
ui->position->setRange(-DBL_MAX, DBL_MAX);
|
||||
ui->position->setUnit(Base::Unit::Length);
|
||||
ui->distance->setRange(0, DBL_MAX);
|
||||
ui->distance->setUnit(Base::Unit::Length);
|
||||
ui->spinEpsilon->setMinimum(0.0001);
|
||||
vp = new ViewProviderCrossSections();
|
||||
|
||||
Base::Vector3d c = bbox.GetCenter();
|
||||
calcPlane(CrossSections::XY, c.z);
|
||||
ui->position->setValue(c.z);
|
||||
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
view = qobject_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
if (view) {
|
||||
view->getViewer()->addViewProvider(vp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys the object and frees any allocated resources
|
||||
*/
|
||||
CrossSections::~CrossSections()
|
||||
{
|
||||
// no need to delete child widgets, Qt does it all for us
|
||||
delete ui;
|
||||
if (view) {
|
||||
view->getViewer()->removeViewProvider(vp);
|
||||
}
|
||||
delete vp;
|
||||
}
|
||||
|
||||
CrossSections::Plane CrossSections::plane() const
|
||||
{
|
||||
if (ui->xyPlane->isChecked())
|
||||
return CrossSections::XY;
|
||||
else if (ui->xzPlane->isChecked())
|
||||
return CrossSections::XZ;
|
||||
else
|
||||
return CrossSections::YZ;
|
||||
}
|
||||
|
||||
void CrossSections::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
else {
|
||||
QDialog::changeEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::keyPressEvent(QKeyEvent* ke)
|
||||
{
|
||||
// The cross-sections dialog is embedded into a task panel
|
||||
// which is a parent widget and will handle the event
|
||||
ke->ignore();
|
||||
}
|
||||
|
||||
void CrossSections::accept()
|
||||
{
|
||||
apply();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void CrossSections::apply()
|
||||
{
|
||||
std::vector<App::DocumentObject*> obj = Gui::Selection().
|
||||
getObjectsOfType(Mesh::Feature::getClassTypeId());
|
||||
|
||||
std::vector<double> d;
|
||||
if (ui->sectionsBox->isChecked())
|
||||
d = getPlanes();
|
||||
else
|
||||
d.push_back(ui->position->value().getValue());
|
||||
double a=0,b=0,c=0;
|
||||
switch (plane()) {
|
||||
case CrossSections::XY:
|
||||
c = 1.0;
|
||||
break;
|
||||
case CrossSections::XZ:
|
||||
b = 1.0;
|
||||
break;
|
||||
case CrossSections::YZ:
|
||||
a = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
bool connectEdges = ui->checkBoxConnect->isChecked();
|
||||
double eps = ui->spinEpsilon->value();
|
||||
|
||||
#if 1 // multi-threaded sections
|
||||
for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
|
||||
const Mesh::MeshObject& mesh = static_cast<Mesh::Feature*>(*it)->Mesh.getValue();
|
||||
|
||||
MeshCore::MeshKernel kernel(mesh.getKernel());
|
||||
kernel.Transform(mesh.getTransform());
|
||||
|
||||
MeshCore::MeshFacetGrid grid(kernel);
|
||||
|
||||
MeshCrossSection cs(kernel, grid, a, b, c, connectEdges, eps);
|
||||
QFuture< std::list<TopoDS_Wire> > future = QtConcurrent::mapped
|
||||
(d, boost::bind(&MeshCrossSection::section, &cs, _1));
|
||||
future.waitForFinished();
|
||||
|
||||
TopoDS_Compound comp;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(comp);
|
||||
|
||||
for (auto ft = future.begin(); ft != future.end(); ++ft) {
|
||||
const std::list<TopoDS_Wire>& w = *ft;
|
||||
for (std::list<TopoDS_Wire>::const_iterator wt = w.begin(); wt != w.end(); ++wt) {
|
||||
if (!wt->IsNull())
|
||||
builder.Add(comp, *wt);
|
||||
}
|
||||
}
|
||||
|
||||
App::Document* doc = (*it)->getDocument();
|
||||
std::string s = (*it)->getNameInDocument();
|
||||
s += "_cs";
|
||||
Part::Feature* section = static_cast<Part::Feature*>
|
||||
(doc->addObject("Part::Feature",s.c_str()));
|
||||
section->Shape.setValue(comp);
|
||||
section->purgeTouched();
|
||||
}
|
||||
#else
|
||||
try {
|
||||
Gui::Command::runCommand(Gui::Command::App, "import Mesh, Part\n");
|
||||
Gui::Command::runCommand(Gui::Command::App, "from FreeCAD import Base\n");
|
||||
|
||||
std::stringstream str;
|
||||
str << "[";
|
||||
for (std::vector<double>::iterator jt = d.begin(); jt != d.end(); ++jt) {
|
||||
double d = *jt;
|
||||
str << "("
|
||||
<< "App.Vector(" << a*d << ", " << b*d << ", " << c*d << "), "
|
||||
<< "App.Vector(" << a << ", " << b << ", " << c << ")"
|
||||
<< "), ";
|
||||
}
|
||||
str << "]";
|
||||
|
||||
QString planes = QString::fromStdString(str.str());
|
||||
for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
|
||||
App::Document* doc = (*it)->getDocument();
|
||||
std::string s = (*it)->getNameInDocument();
|
||||
s += "_cs";
|
||||
Gui::Command::runCommand(Gui::Command::App, QString::fromLatin1(
|
||||
"points=FreeCAD.getDocument(\"%1\").%2.Mesh.crossSections(%3, %4, %5)\n"
|
||||
"wires=[]\n"
|
||||
"for i in points:\n"
|
||||
" wires.extend([Part.makePolygon(j) for j in i])\n")
|
||||
.arg(QLatin1String(doc->getName()))
|
||||
.arg(QLatin1String((*it)->getNameInDocument()))
|
||||
.arg(planes)
|
||||
.arg(eps)
|
||||
.arg(connectEdges ? QLatin1String("True") : QLatin1String("False"))
|
||||
.toLatin1());
|
||||
|
||||
Gui::Command::runCommand(Gui::Command::App, QString::fromLatin1(
|
||||
"comp=Part.Compound(wires)\n"
|
||||
"slice=FreeCAD.getDocument(\"%1\").addObject(\"Part::Feature\",\"%2\")\n"
|
||||
"slice.Shape=comp\n"
|
||||
"slice.purgeTouched()\n"
|
||||
"del slice,comp,wires,points")
|
||||
.arg(QLatin1String(doc->getName()))
|
||||
.arg(QLatin1String(s.c_str())).toLatin1());
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
QMessageBox::critical(this, tr("Failure"), QString::fromLatin1(e.what()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrossSections::on_xyPlane_clicked()
|
||||
{
|
||||
Base::Vector3d c = bbox.GetCenter();
|
||||
ui->position->setValue(c.z);
|
||||
if (!ui->sectionsBox->isChecked()) {
|
||||
calcPlane(CrossSections::XY, c.z);
|
||||
}
|
||||
else {
|
||||
double dist = bbox.LengthZ() / ui->countSections->value();
|
||||
if (!ui->checkBothSides->isChecked())
|
||||
dist *= 0.5f;
|
||||
ui->distance->setValue(dist);
|
||||
calcPlanes(CrossSections::XY);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::on_xzPlane_clicked()
|
||||
{
|
||||
Base::Vector3d c = bbox.GetCenter();
|
||||
ui->position->setValue(c.y);
|
||||
if (!ui->sectionsBox->isChecked()) {
|
||||
calcPlane(CrossSections::XZ, c.y);
|
||||
}
|
||||
else {
|
||||
double dist = bbox.LengthY() / ui->countSections->value();
|
||||
if (!ui->checkBothSides->isChecked())
|
||||
dist *= 0.5f;
|
||||
ui->distance->setValue(dist);
|
||||
calcPlanes(CrossSections::XZ);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::on_yzPlane_clicked()
|
||||
{
|
||||
Base::Vector3d c = bbox.GetCenter();
|
||||
ui->position->setValue(c.x);
|
||||
if (!ui->sectionsBox->isChecked()) {
|
||||
calcPlane(CrossSections::YZ, c.x);
|
||||
}
|
||||
else {
|
||||
double dist = bbox.LengthX() / ui->countSections->value();
|
||||
if (!ui->checkBothSides->isChecked())
|
||||
dist *= 0.5f;
|
||||
ui->distance->setValue(dist);
|
||||
calcPlanes(CrossSections::YZ);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::on_position_valueChanged(double v)
|
||||
{
|
||||
if (!ui->sectionsBox->isChecked()) {
|
||||
calcPlane(plane(), v);
|
||||
}
|
||||
else {
|
||||
calcPlanes(plane());
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::on_sectionsBox_toggled(bool b)
|
||||
{
|
||||
if (b) {
|
||||
on_countSections_valueChanged(ui->countSections->value());
|
||||
}
|
||||
else {
|
||||
CrossSections::Plane type = plane();
|
||||
Base::Vector3d c = bbox.GetCenter();
|
||||
double value = 0;
|
||||
switch (type) {
|
||||
case CrossSections::XY:
|
||||
value = c.z;
|
||||
break;
|
||||
case CrossSections::XZ:
|
||||
value = c.y;
|
||||
break;
|
||||
case CrossSections::YZ:
|
||||
value = c.x;
|
||||
break;
|
||||
}
|
||||
|
||||
ui->position->setValue(value);
|
||||
calcPlane(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossSections::on_checkBothSides_toggled(bool b)
|
||||
{
|
||||
double d = ui->distance->value().getValue();
|
||||
d = b ? 2.0 * d : 0.5 * d;
|
||||
ui->distance->setValue(d);
|
||||
calcPlanes(plane());
|
||||
}
|
||||
|
||||
void CrossSections::on_countSections_valueChanged(int v)
|
||||
{
|
||||
CrossSections::Plane type = plane();
|
||||
double dist = 0;
|
||||
switch (type) {
|
||||
case CrossSections::XY:
|
||||
dist = bbox.LengthZ() / v;
|
||||
break;
|
||||
case CrossSections::XZ:
|
||||
dist = bbox.LengthY() / v;
|
||||
break;
|
||||
case CrossSections::YZ:
|
||||
dist = bbox.LengthX() / v;
|
||||
break;
|
||||
}
|
||||
if (!ui->checkBothSides->isChecked())
|
||||
dist *= 0.5f;
|
||||
ui->distance->setValue(dist);
|
||||
calcPlanes(type);
|
||||
}
|
||||
|
||||
void CrossSections::on_distance_valueChanged(double)
|
||||
{
|
||||
calcPlanes(plane());
|
||||
}
|
||||
|
||||
void CrossSections::calcPlane(Plane type, double pos)
|
||||
{
|
||||
double bound[4];
|
||||
switch (type) {
|
||||
case XY:
|
||||
bound[0] = bbox.MinX;
|
||||
bound[1] = bbox.MaxX;
|
||||
bound[2] = bbox.MinY;
|
||||
bound[3] = bbox.MaxY;
|
||||
break;
|
||||
case XZ:
|
||||
bound[0] = bbox.MinX;
|
||||
bound[1] = bbox.MaxX;
|
||||
bound[2] = bbox.MinZ;
|
||||
bound[3] = bbox.MaxZ;
|
||||
break;
|
||||
case YZ:
|
||||
bound[0] = bbox.MinY;
|
||||
bound[1] = bbox.MaxY;
|
||||
bound[2] = bbox.MinZ;
|
||||
bound[3] = bbox.MaxZ;
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<double> d;
|
||||
d.push_back(pos);
|
||||
makePlanes(type, d, bound);
|
||||
}
|
||||
|
||||
void CrossSections::calcPlanes(Plane type)
|
||||
{
|
||||
double bound[4];
|
||||
switch (type) {
|
||||
case XY:
|
||||
bound[0] = bbox.MinX;
|
||||
bound[1] = bbox.MaxX;
|
||||
bound[2] = bbox.MinY;
|
||||
bound[3] = bbox.MaxY;
|
||||
break;
|
||||
case XZ:
|
||||
bound[0] = bbox.MinX;
|
||||
bound[1] = bbox.MaxX;
|
||||
bound[2] = bbox.MinZ;
|
||||
bound[3] = bbox.MaxZ;
|
||||
break;
|
||||
case YZ:
|
||||
bound[0] = bbox.MinY;
|
||||
bound[1] = bbox.MaxY;
|
||||
bound[2] = bbox.MinZ;
|
||||
bound[3] = bbox.MaxZ;
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<double> d = getPlanes();
|
||||
makePlanes(type, d, bound);
|
||||
}
|
||||
|
||||
std::vector<double> CrossSections::getPlanes() const
|
||||
{
|
||||
int count = ui->countSections->value();
|
||||
double pos = ui->position->value().getValue();
|
||||
double stp = ui->distance->value().getValue();
|
||||
bool both = ui->checkBothSides->isChecked();
|
||||
|
||||
std::vector<double> d;
|
||||
if (both) {
|
||||
double start = pos-0.5f*(count-1)*stp;
|
||||
for (int i=0; i<count; i++) {
|
||||
d.push_back(start+i*stp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<count; i++) {
|
||||
d.push_back(pos+i*stp);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void CrossSections::makePlanes(Plane type, const std::vector<double>& d, double bound[4])
|
||||
{
|
||||
std::vector<Base::Vector3f> points;
|
||||
for (std::vector<double>::const_iterator it = d.begin(); it != d.end(); ++it) {
|
||||
Base::Vector3f v[4];
|
||||
switch (type) {
|
||||
case XY:
|
||||
v[0].Set(bound[0],bound[2],*it);
|
||||
v[1].Set(bound[1],bound[2],*it);
|
||||
v[2].Set(bound[1],bound[3],*it);
|
||||
v[3].Set(bound[0],bound[3],*it);
|
||||
break;
|
||||
case XZ:
|
||||
v[0].Set(bound[0],*it,bound[2]);
|
||||
v[1].Set(bound[1],*it,bound[2]);
|
||||
v[2].Set(bound[1],*it,bound[3]);
|
||||
v[3].Set(bound[0],*it,bound[3]);
|
||||
break;
|
||||
case YZ:
|
||||
v[0].Set(*it,bound[0],bound[2]);
|
||||
v[1].Set(*it,bound[1],bound[2]);
|
||||
v[2].Set(*it,bound[1],bound[3]);
|
||||
v[3].Set(*it,bound[0],bound[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
points.push_back(v[0]);
|
||||
points.push_back(v[1]);
|
||||
points.push_back(v[2]);
|
||||
points.push_back(v[3]);
|
||||
points.push_back(v[0]);
|
||||
}
|
||||
vp->setCoords(points);
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
TaskCrossSections::TaskCrossSections(const Base::BoundBox3d& bb)
|
||||
{
|
||||
widget = new CrossSections(bb);
|
||||
taskbox = new Gui::TaskView::TaskBox(
|
||||
Gui::BitmapFactory().pixmap("Part_CrossSections"),
|
||||
widget->windowTitle(), true, 0);
|
||||
taskbox->groupLayout()->addWidget(widget);
|
||||
Content.push_back(taskbox);
|
||||
}
|
||||
|
||||
TaskCrossSections::~TaskCrossSections()
|
||||
{
|
||||
// automatically deleted in the sub-class
|
||||
}
|
||||
|
||||
bool TaskCrossSections::accept()
|
||||
{
|
||||
widget->accept();
|
||||
return (widget->result() == QDialog::Accepted);
|
||||
}
|
||||
|
||||
void TaskCrossSections::clicked(int id)
|
||||
{
|
||||
if (id == QDialogButtonBox::Apply) {
|
||||
widget->apply();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_CrossSections.cpp"
|
||||
102
src/Mod/MeshPart/Gui/CrossSections.h
Normal file
102
src/Mod/MeshPart/Gui/CrossSections.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2019 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* 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 MESHPARTGUI_CROSSSECTIONS_H
|
||||
#define MESHPARTGUI_CROSSSECTIONS_H
|
||||
|
||||
#include <Gui/TaskView/TaskDialog.h>
|
||||
#include <Gui/TaskView/TaskView.h>
|
||||
#include <Base/BoundBox.h>
|
||||
#include <QDialog>
|
||||
#include <QPointer>
|
||||
|
||||
namespace Gui {
|
||||
class View3DInventor;
|
||||
}
|
||||
|
||||
namespace MeshPartGui {
|
||||
|
||||
class ViewProviderCrossSections;
|
||||
class Ui_CrossSections;
|
||||
class CrossSections : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum Plane { XY, XZ, YZ };
|
||||
|
||||
public:
|
||||
CrossSections(const Base::BoundBox3d& bb, QWidget* parent = 0, Qt::WindowFlags fl = 0);
|
||||
~CrossSections();
|
||||
void accept();
|
||||
void apply();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e);
|
||||
void keyPressEvent(QKeyEvent*);
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_xyPlane_clicked();
|
||||
void on_xzPlane_clicked();
|
||||
void on_yzPlane_clicked();
|
||||
void on_position_valueChanged(double);
|
||||
void on_distance_valueChanged(double);
|
||||
void on_countSections_valueChanged(int);
|
||||
void on_checkBothSides_toggled(bool);
|
||||
void on_sectionsBox_toggled(bool);
|
||||
|
||||
private:
|
||||
std::vector<double> getPlanes() const;
|
||||
void calcPlane(Plane, double);
|
||||
void calcPlanes(Plane/*, double, bool, int*/);
|
||||
void makePlanes(Plane, const std::vector<double>&, double[4]);
|
||||
Plane plane() const;
|
||||
|
||||
private:
|
||||
Ui_CrossSections* ui;
|
||||
Base::BoundBox3d bbox;
|
||||
ViewProviderCrossSections* vp;
|
||||
QPointer<Gui::View3DInventor> view;
|
||||
};
|
||||
|
||||
class TaskCrossSections : public Gui::TaskView::TaskDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskCrossSections(const Base::BoundBox3d& bb);
|
||||
~TaskCrossSections();
|
||||
|
||||
public:
|
||||
bool accept();
|
||||
void clicked(int id);
|
||||
|
||||
virtual QDialogButtonBox::StandardButtons getStandardButtons() const
|
||||
{ return QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel; }
|
||||
|
||||
private:
|
||||
CrossSections* widget;
|
||||
Gui::TaskView::TaskBox* taskbox;
|
||||
};
|
||||
|
||||
} // namespace MeshPartGui
|
||||
|
||||
#endif // MESHPARTGUI_CROSSSECTIONS_H
|
||||
192
src/Mod/MeshPart/Gui/CrossSections.ui
Normal file
192
src/Mod/MeshPart/Gui/CrossSections.ui
Normal file
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MeshPartGui::CrossSections</class>
|
||||
<widget class="QDialog" name="MeshPartGui::CrossSections">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>309</width>
|
||||
<height>269</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Cross sections</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="planeBox">
|
||||
<property name="title">
|
||||
<string>Guiding plane</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="xyPlane">
|
||||
<property name="text">
|
||||
<string>XY</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="xzPlane">
|
||||
<property name="text">
|
||||
<string>XZ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QRadioButton" name="yzPlane">
|
||||
<property name="text">
|
||||
<string>YZ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Position:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::QuantitySpinBox" name="position">
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="sectionsBox">
|
||||
<property name="title">
|
||||
<string>Sections</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBothSides">
|
||||
<property name="text">
|
||||
<string>On both sides</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Count</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="countSections">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>2000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Distance:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="distance">
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxConnect">
|
||||
<property name="text">
|
||||
<string>Connect edges if distance less than</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinEpsilon">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.020000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>xyPlane</tabstop>
|
||||
<tabstop>xzPlane</tabstop>
|
||||
<tabstop>yzPlane</tabstop>
|
||||
<tabstop>position</tabstop>
|
||||
<tabstop>sectionsBox</tabstop>
|
||||
<tabstop>checkBothSides</tabstop>
|
||||
<tabstop>countSections</tabstop>
|
||||
<tabstop>distance</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>checkBoxConnect</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>spinEpsilon</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>75</x>
|
||||
<y>230</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>254</x>
|
||||
<y>231</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user