-
-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use flying edges for mesh generation #1741
Conversation
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Here are the build results |
Signed-off-by: Perminder <[email protected]>
avogadro/qtgui/meshgenerator.cpp
Outdated
// Add triangles | ||
const char* caseTri = caseTriangles[caseId]; // size 16 | ||
for(int idx = 0; caseTri[idx] != -1; idx += 3) | ||
{ | ||
|
||
|
||
size_t globalIdx = globalIdxs[caseTri[idx]]; | ||
size_t globalIdx1 = globalIdxs[caseTri[idx + 1]]; | ||
size_t globalIdx2 = globalIdxs[caseTri[idx + 2]]; | ||
|
||
|
||
|
||
m_vertices.push_back(points[globalIdx]); | ||
m_vertices.push_back(points[globalIdx1]); | ||
m_vertices.push_back(points[globalIdx2]); | ||
|
||
m_normals.push_back(normals[globalIdx]); | ||
m_normals.push_back(normals[globalIdx1]); | ||
m_normals.push_back(normals[globalIdx2]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do cleanups in the code once I start getting the expected output. Also tagging @matterhorn103 and @aerkiaga for their inputs. Let me know if you guys have any feedbacks/suggestions? |
Signed-off-by: Perminder <[email protected]>
@perminder-17 I'm afraid rendering is something I know absolutely nothing about |
Aah... Thanks for your reply. No problem, I figured that out. It was like, we need to pass the triangles as well. |
Great, I'll start a review tomorrow |
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Here are the build results |
This pull request has been mentioned on Avogadro Discussion. There might be relevant details there: https://discuss.avogadro.cc/t/november-2024-live-updates/6446/1 |
Signed-off-by: Perminder <[email protected]>
… variables Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Hi everyone, Here's the results after the implementation of flying-edges. requesting @ghutchis for the review, still needs some cleanups in the code, removal of unnecessary codeblocks and variables. Marching cube takes 1.5 minutes to render the meshFlying edges takes only 3 seconds to render it. (could vary with different systems)Also it produces a little smoother mesh (left->flying edges, right->marching cubes)After Laplacian smoothing we have: |
I'm happy to help do some performance tests - I'm not seeing the "Time taken" code at the moment in your pull request. I have a range of molecule sizes and resolutions to give this a good benchmark. Doing a rough stopwatch count, I'm seeing ~2-2.5x performance improvement, but that's including the whole thing, not just mesh generation. I can say that with caffeine as an example, the flying edges version appears instantly on my MacBook even at "Very High" resolution. With a larger molecule, it's going from ~10s to display the VdW mesh to ~4.5s. If you want to send me the patch you used for timing, I'd be happy to perform some real benchmarks tonight in release mode. |
Thanks @cryos for your insights. Really helps a lot and also thanks @ghutchis for the help in performance testing. If I could say what code I used for checking the time, I used the chrono library https://en.cppreference.com/w/cpp/chrono/high_resolution_clock/now If you include header #include <chrono> // at the top (header file)
// Rest code remains the same
// Replace the run function to test
void MeshGenerator::run() // you can directly copy the run() function and paste it with the original ones
{
if (!m_cube || !m_mesh) {
qDebug() << "No mesh or cube set - nothing to find isosurface of…";
return;
}
m_mesh->setStable(false);
m_mesh->clear();
auto start = std::chrono::high_resolution_clock::now();
FlyingEdgesAlgorithmPass1();
FlyingEdgesAlgorithmPass2();
FlyingEdgesAlgorithmPass3();
FlyingEdgesAlgorithmPass4();
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
qDebug() << "Flying Edges Time: " << elapsed.count() << " s";
m_mesh->setVertices(points);
m_mesh->setNormals(normals);
m_mesh->setTriangles(tris);
m_mesh->smooth(m_passes);
m_mesh->setStable(true);
points.resize(0);
normals.resize(0);
tris.resize(0);
} also, if you want to test and compare the time with marching cubes. here how you can check the time taken for marching cubes as well. OLD CODE#include <chrono> // similarly include this
// just a little changes in the run function
void MeshGenerator::run()
{
// rest code remains the same.
auto start = std::chrono::high_resolution_clock::now(); // add this
for (int i = 0; i < m_dim.x() - 1; ++i) {
for (int j = 0; j < m_dim.y() - 1; ++j) {
for (int k = 0; k < m_dim.z() - 1; ++k) {
marchingCube(Vector3i(i, j, k));
}
}
if (m_vertices.capacity() < m_vertices.size() + m_dim.y() * m_dim.x() * 3) {
m_vertices.reserve(m_vertices.capacity() * 2);
m_normals.reserve(m_normals.capacity() * 2);
}
emit progressValueChanged(i);
}
// add this
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
qDebug() << "Marching Cube Timing: " << elapsed.count() << " s";
// rest code remains the same
}
|
Also, after the merge of this, I can handle the merge conflicts (if any) in this PR #1692. If we add a progressive refinement, it won't take even 4.5 seconds to render and will generate the mesh in no time. Do let me know if everything looks fine in the results, I could do many cleanups in the code (added too many debugging peices, unnecessary functions, variables haha). Still needs some formatting and cleanups. |
Signed-off-by: Perminder <[email protected]>
Btw @ghutchis I just noticed one bug (when you click on the calculate button twice for the same mesh generation it takes more time). It's because we were not clearing the memory. In the above commit I have fixed that as well. Sorry for the oversight. |
I'm seeing ~2x - 2.2x speedup for mesh generation on a range of 7 molecules with 0.05Å resolution. It depends a lot on the size of the molecule and the shape (e.g., smaller molecules and more spherical / cubic see the greatest speedup). I'll do a bit of profiling, but of course this is also the single-core implementation (vs. parallel) |
I also checked the number of vertices for marching cubes vs. flying edges. It's getting exactly 6.0 times fewer vertices, so the mesh is much smaller at the same resolution (and thus faster to render, less memory, etc.) Even on a really big DNA chunk, I'm seeing ~0.1s for mesh creation with the "automatic" resolution. (And yes, the progressive refinement could be a help here too.) |
Signed-off-by: Perminder <[email protected]>
Here are the build results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly some small things with the comments and documentation consistency.
avogadro/core/cube.h
Outdated
std::array<float, 3> computeGradient(int i, int j, int k) const; | ||
|
||
/** | ||
* Get the values of the eight corners of a cube cell. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure I understand from the docs here. I've got some index (i, j, k) into the cube. What's the "cell" of the cube? Is it just going to get me the corners of the entire cube? If so, why do I need i, j, k parameters?
avogadro/core/cube.h
Outdated
float getData(int i, int j, int k) const; | ||
|
||
/** | ||
* Get the positions of the eight corners of a cube cell. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, what's the "cell" here? Are these the min, max of the corners of the cube? If so, why do you need the i, j, k?
avogadro/core/mesh.h
Outdated
@@ -228,6 +232,7 @@ class AVOGADROCORE_EXPORT Mesh | |||
Core::Array<Vector3f> m_vertices; | |||
Core::Array<Vector3f> m_normals; | |||
Core::Array<Color3f> m_colors; | |||
Core::Array<Vector3f> tris; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use m_triangles
to be consistent with the other variables.
@@ -107,34 +173,44 @@ class AVOGADROQTGUI_EXPORT MeshGenerator : public QThread | |||
int progressMaximum() { return m_progmax; } | |||
|
|||
signals: | |||
|
|||
/** | |||
* The current value of the calculation's progress. | |||
*/ | |||
void progressValueChanged(int); | |||
|
|||
protected: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep this? Sometimes it's helpful to get normals for arbitrary points.
avogadro/qtgui/meshgenerator.h
Outdated
*/ | ||
Vector3f normal(const Vector3f& pos); | ||
|
||
unsigned long duplicate(const Vector3i& c, const Vector3f& pos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs documentation.
avogadro/qtgui/meshgenerator.h
Outdated
std::vector<unsigned char> cubeCases; //size ((m_dim.x() - 1) * (m_dim.y() - 1) * (m_dim.z() - 1)) | ||
std::vector<int> triCounter; // size ((m_dim.y() - 1) * (m_dim.z() - 1)) | ||
std::vector<unsigned char> edgeCases; // size ((m_dim.x() - 1) * (m_dim.y()) * (m_dim.z())) | ||
Core::Array<Vector3f> tris; // triangles of a mesh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove any un-used variables (from Marching Cubes)
Please also use m_triangles
(or similar) to keep consistent with member variables through the rest of the code.
avogadro/qtgui/meshgenerator.h
Outdated
static const unsigned char numTris[256]; | ||
static const bool isCut[256][12]; | ||
static const char caseTriangles[256][16]; | ||
static const unsigned char edgeVertices[12][2]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The static analysis says these class members are never used?
If they're used, please keep to m_edgeVertices
etc. for consistency.
Signed-off-by: Perminder <[email protected]>
Signed-off-by: Perminder <[email protected]>
Here are the build results |
1 similar comment
Here are the build results |
|
||
std::array<std::array<float, 3>, 8> pos; | ||
|
||
float xpos = m_min.x() + (i * m_spacing.x()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry - I still don't understand what this method is supposed to do.
xpos, ypos, zpos are the real-space versions of i, j, k indices
.. so the method gives me a cube that's one step in each of the x, y, z directions?
If so, shouldn't that be the doc string?
@@ -23,9 +24,7 @@ class Mutex; | |||
/** | |||
* @class Cube cube.h <avogadro/core/cube.h> | |||
* @brief Provide a data structure for regularly spaced 3D grids. | |||
* @author Marcus D. Hanwell |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@author Marcus D. Hanwell
@author Perminder Singh
avogadro/core/mesh.h
Outdated
@@ -138,14 +142,6 @@ class AVOGADROCORE_EXPORT Mesh | |||
*/ | |||
const Core::Array<Vector3f>& normals() const; | |||
|
|||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand why you're removing numNormals()
which seems like a useful method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But i was unable to find where we were using numnormals() in our actual code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not, currently. But since this is a core
class, it's also API for future development.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly resolved - please note the other comments, thanks.
Signed-off-by: Perminder <[email protected]>
Here are the build results |
1 similar comment
Here are the build results |
Signed-off-by: Geoff Hutchison <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed these last time. I think this fixes the compile on Windows.
avogadro/qtgui/meshgenerator.cpp
Outdated
ge2.xstart += isCutCase[4]; | ||
ge2.ystart += isCutCase[7]; | ||
} | ||
if(isXEnd and isYEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&
not and
- this is the build failure on Windows
avogadro/qtgui/meshgenerator.cpp
Outdated
{ | ||
ge1.zstart += isCutCase[11]; | ||
} | ||
if(isXEnd and isZEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same - use &&
avogadro/qtgui/meshgenerator.cpp
Outdated
{ | ||
ge2.ystart += isCutCase[5]; | ||
} | ||
if(isYEnd and isZEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&
avogadro/qtgui/meshgenerator.cpp
Outdated
if(isCutCase[11]) | ||
{ | ||
int idx = ge1.zstart + z1counter; | ||
if(isXEnd and isYEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&
not and
avogadro/qtgui/meshgenerator.cpp
Outdated
if(isCutCase[5]) | ||
{ | ||
int idx = ge2.ystart + y2counter; | ||
if(isXEnd and isZEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&
not and
avogadro/qtgui/meshgenerator.cpp
Outdated
if(isCutCase[6]) | ||
{ | ||
int idx = ge3.xstart + x3counter; | ||
if(isYEnd and isZEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&
not and
Signed-off-by: Perminder Singh <[email protected]>
Done @ghutchis |
Here are the build results |
Here are the build results |
Resolves issue : #901
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.