From 0d782797a082ebdba039530861abab9a9d8fb6a5 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 3 Mar 2012 18:24:49 +0100 Subject: [PATCH] Port Menger sponge algorithm to C++ --- src/Mod/Sandbox/Gui/Command.cpp | 157 ++++++++++++++++++++++++++ src/Mod/Sandbox/Gui/Workbench.cpp | 3 +- src/Mod/TemplatePyMod/MengerSponge.py | 11 +- 3 files changed, 167 insertions(+), 4 deletions(-) diff --git a/src/Mod/Sandbox/Gui/Command.cpp b/src/Mod/Sandbox/Gui/Command.cpp index d46b294e2..4f2976323 100644 --- a/src/Mod/Sandbox/Gui/Command.cpp +++ b/src/Mod/Sandbox/Gui/Command.cpp @@ -45,6 +45,7 @@ # include # include # include +# include # include # include # include @@ -71,6 +72,7 @@ #include #include #include +#include #include "Workbench.h" @@ -1247,6 +1249,160 @@ void CmdSandboxFlyViewer::activated(int iMsg) viewer->setSceneGraph(new SoCone); } +// ------------------------------------------------------------------------------- + +DEF_STD_CMD(CmdMengerSponge); + +CmdMengerSponge::CmdMengerSponge() + :Command("Sandbox_MengerSponge") +{ + sAppModule = "Sandbox"; + sGroup = QT_TR_NOOP("Sandbox"); + sMenuText = QT_TR_NOOP("Menger sponge"); + sToolTipText = QT_TR_NOOP("Menger sponge"); + sWhatsThis = QT_TR_NOOP("Menger sponge"); + sStatusTip = QT_TR_NOOP("Menger sponge"); +} + +struct Param { + int level; + float x,y,z; + Param(int l, float x, float y, float z) + : level(l), x(x), y(y), z(z) + { + } +}; + +typedef Base::Reference MeshObjectRef; + +MeshObjectRef globalBox; + +// Create a Box and Place it a coords (x,y,z) +MeshObjectRef PlaceBox(float x, float y, float z) +{ + MeshObjectRef mesh = new Mesh::MeshObject(*globalBox); + Base::Matrix4D m; + m.move(x,y,z); + mesh->getKernel().Transform(m); + return mesh; +} + +MeshObjectRef Sierpinski(int level, float x0, float y0, float z0) +{ + float boxnums = std::pow(3.0f,level); + float thirds = boxnums / 3; + float twothirds = thirds * 2; + + QList rangerx, rangery, rangerz; + if (level == 0) { + rangerx << x0; + rangery << y0; + rangerz << z0; + } + else { + rangerx << x0 << (x0 + thirds) << (x0 + twothirds); + rangery << y0 << (y0 + thirds) << (y0 + twothirds); + rangerz << z0 << (z0 + thirds) << (z0 + twothirds); + } + + int block = 1; + QList skip; skip << 5 << 11 << 13 << 14 << 15 << 17 << 23; + MeshObjectRef mesh = new Mesh::MeshObject(); + + for (QList::iterator i = rangerx.begin(); i != rangerx.end(); ++i) { + for (QList::iterator j = rangery.begin(); j != rangery.end(); ++j) { + for (QList::iterator k = rangerz.begin(); k != rangerz.end(); ++k) { + if (!skip.contains(block)) { + if (level > 0) + mesh->addMesh(*Sierpinski(level-1,*i,*j,*k)); + else + mesh->addMesh(*PlaceBox(*i,*j,*k)); + } + block++; + } + } + } + + return mesh; +} + +MeshObjectRef runSierpinski(const Param& p) +{ + return Sierpinski(p.level,p.x,p.y,p.z); +} + +void CmdMengerSponge::activated(int iMsg) +{ + int level = QInputDialog::getInteger(Gui::getMainWindow(), + QString::fromAscii("Menger sponge"), + QString::fromAscii("Recursion depth:"), + 3, 1, 5); + float x0=0,y0=0,z0=0; + + globalBox = Mesh::MeshObject::createCube(1,1,1); + + float boxnums = std::pow(3.0f,level); + float thirds = boxnums / 3; + float twothirds = thirds * 2; + + QList rangerx, rangery, rangerz; + rangerx << x0 << (x0 + thirds) << (x0 + twothirds); + rangery << y0 << (y0 + thirds) << (y0 + twothirds); + rangerz << z0 << (z0 + thirds) << (z0 + twothirds); + + int block = 1; + QList skip; skip << 5 << 11 << 13 << 14 << 15 << 17 << 23; + + // collect the arguments for the algorithm in a list + QList args; + + for (QList::iterator i = rangerx.begin(); i != rangerx.end(); ++i) { + for (QList::iterator j = rangery.begin(); j != rangery.end(); ++j) { + for (QList::iterator k = rangerz.begin(); k != rangerz.end(); ++k) { + if (!skip.contains(block)) { + args << Param(level-1, *i, *j, *k); + } + block++; + } + } + } + + QFuture future = QtConcurrent::mapped(args, runSierpinski); + + QFutureWatcher watcher; + watcher.setFuture(future); + + // keep it responsive during computation + QEventLoop loop; + QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + Mesh::MeshObject mesh; + App::Document* doc = App::GetApplication().newDocument(); + for (QFuture::const_iterator it = future.begin(); it != future.end(); ++it) { + mesh.addMesh(**it); + (*it)->clear(); + } + + + MeshCore::MeshKernel& kernel = mesh.getKernel(); + + // remove duplicated points + MeshCore::MeshFixDuplicatePoints(kernel).Fixup(); + + // remove internal facets + MeshCore::MeshEvalInternalFacets eval(kernel); + eval.Evaluate(); + kernel.DeleteFacets(eval.GetIndices()); + + // repair neighbourhood + kernel.RebuildNeighbours(); + + Mesh::Feature* feature = static_cast(doc->addObject("Mesh::Feature","MengerSponge")); + feature->Mesh.setValue(mesh); + feature->purgeTouched(); +} + void CreateSandboxCommands(void) { @@ -1279,4 +1435,5 @@ void CreateSandboxCommands(void) rcCmdMgr.addCommand(new CmdSandboxPlaneViewer()); rcCmdMgr.addCommand(new CmdSandboxExaminerViewer()); rcCmdMgr.addCommand(new CmdSandboxFlyViewer()); + rcCmdMgr.addCommand(new CmdMengerSponge()); } diff --git a/src/Mod/Sandbox/Gui/Workbench.cpp b/src/Mod/Sandbox/Gui/Workbench.cpp index f7746225a..c87385295 100644 --- a/src/Mod/Sandbox/Gui/Workbench.cpp +++ b/src/Mod/Sandbox/Gui/Workbench.cpp @@ -72,7 +72,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Sandbox_MeshLoaderFuture" << "Sandbox_MeshTestJob" << "Sandbox_MeshTestRef" - << "Sandbox_CryptographicHash"; + << "Sandbox_CryptographicHash" + << "Sandbox_MengerSponge"; Gui::MenuItem* widg = new Gui::MenuItem; root->insertItem(item, widg); diff --git a/src/Mod/TemplatePyMod/MengerSponge.py b/src/Mod/TemplatePyMod/MengerSponge.py index 67beb5921..12741bedd 100644 --- a/src/Mod/TemplatePyMod/MengerSponge.py +++ b/src/Mod/TemplatePyMod/MengerSponge.py @@ -8,11 +8,16 @@ import threading import Mesh, MeshGui from FreeCAD import Base +# Create a global mesh and make copies of them +# This makes the algorithm faster by ~60%. +box = Mesh.createBox(1,1,1) + # Create a Box and Place it a coords (x,y,z) def PlaceBox(x,y,z): - box = Mesh.createBox(1,1,1) - box.translate(x,y,z) - return box + global box + mbox=box.copy() + mbox.translate(x,y,z) + return mbox def Sierpinski(level,x0,y0,z0): #print threading.current_thread().name