Skip to content

Commit ed0b58b

Browse files
Green tests! (#3297)
* Green tests! * Add `MR_BIND_TEMPLATE`. Fail the build if the tests fail.
1 parent 7b9ca5c commit ed0b58b

11 files changed

+186
-13
lines changed

.github/workflows/build-test-ubuntu-x64.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
repository: MeshInspector/mrbind
122122
path: _mrbind
123123
token: ${{ secrets.BUILD_MACHINE_TOKEN }}
124-
ref: 83a53a39c1f0dbb5564a47355026406ead99946b
124+
ref: b8448867e913efe0561404597b575588a20b674b
125125

126126
- name: Compile MRBind
127127
if: ${{ matrix.os == 'ubuntu24' }}
@@ -170,7 +170,7 @@ jobs:
170170
LD_LIBRARY_PATH: .
171171
USE_MESHLIB2_PY: 1
172172
working-directory: build/${{ matrix.config }}/bin
173-
run: python3 ./../../../scripts/run_python_test_script.py -d '../test_python' || true
173+
run: python3 ./../../../scripts/run_python_test_script.py -d '../test_python'
174174

175175
- name: Collect Timings
176176
run: ./scripts/devops/collect_timing_logs.sh ${{matrix.os}} ${{matrix.config}} "${{matrix.compiler}}"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
-I_mrbind/include -Wno-deprecated-declarations -Wno-implicitly-unsigned-literal -fPIC -DMR_COMPILING_PB11_BINDINGS -DMRBIND_HEADER='<mrbind/targets/pybind11.h>' -DMB_PB11_MODULE_NAME=mrmeshpy -DMB_PB11_ADJUST_NAMES='"s/\\bMR:://g;s/\\bvec_\\b/vec/"' -DMB_PB11_ENABLE_CXX_STYLE_CONTAINER_METHODS
1+
-I_mrbind/include -Wno-deprecated-declarations -Wno-implicitly-unsigned-literal -fPIC -DMR_COMPILING_PB11_BINDINGS -DMRBIND_HEADER='<mrbind/targets/pybind11.h>' -DMB_PB11_MODULE_NAME=mrmeshpy -DMB_PB11_ADJUST_NAMES='"s/\\bMR::Extra:://g;s/\\bMR:://g;s/\\bvec_\\b/vec/"' -DMB_PB11_ENABLE_CXX_STYLE_CONTAINER_METHODS

scripts/mrbind/generate.mk

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,35 +28,40 @@ DEPS_BASE_DIR := .
2828
# ]
2929

3030
MODULE_OUTPUT_DIR := $(MESHLIB_SHLIB_DIR)/meshlib2
31-
MRBIND_FLAGS := $(file <$(makefile_dir)/mrbind_flags.txt)
3231

3332
# Those variables are for mrbind/scripts/apply_to_files.mk
3433
INPUT_DIRS := $(addprefix $(makefile_dir)/../../source/,MRMesh MRSymbolMesh) $(makefile_dir)
3534
INPUT_FILES_BLACKLIST := $(file <$(makefile_dir)/input_file_blacklist.txt)
3635
OUTPUT_DIR := build/binds
3736
INPUT_GLOBS := *.h
38-
MRBIND := $(MRBIND_EXE) $(MRBIND_FLAGS)
37+
MRBIND := $(MRBIND_EXE)
38+
MRBIND_FLAGS := $(file <$(makefile_dir)/mrbind_flags.txt)
39+
MRBIND_FLAGS_FOR_EXTRA_INPUTS := $(file <$(makefile_dir)/mrbind_flags_for_helpers.txt)
3940
COMPILER_FLAGS := $(file <$(makefile_dir)/common_compiler_parser_flags.txt) $(shell pkg-config --cflags python3-embed) -I. -I$(DEPS_BASE_DIR)/include -I$(makefile_dir)/../../source
4041
COMPILER_FLAGS_LIBCLANG := $(file <$(makefile_dir)/parser_only_flags.txt)
4142
COMPILER := $(CXX) $(file <$(makefile_dir)/compiler_only_flags.txt) -I$(MRBIND_SOURCE)/include
4243
LINKER_OUTPUT := $(MODULE_OUTPUT_DIR)/mrmeshpy$(shell python3-config --extension-suffix)
4344
LINKER := $(CXX) -fuse-ld=lld
4445
LINKER_FLAGS := -Wl,-rpath='$$ORIGIN/..:$$ORIGIN' $(shell pkg-config --libs python3-embed) -L$(DEPS_BASE_DIR)/lib -L$(MESHLIB_SHLIB_DIR) -lMRMesh -lMRSymbolMesh -shared $(file <$(makefile_dir)/linker_flags.txt)
4546
NUM_FRAGMENTS := 4
47+
EXTRA_INPUT_SOURCES := $(makefile_dir)/helpers.cpp
4648

4749
override mrbind_vars = $(subst $,$$$$, \
4850
INPUT_DIRS=$(call quote,$(INPUT_DIRS)) \
4951
INPUT_FILES_BLACKLIST=$(call quote,$(INPUT_FILES_BLACKLIST)) \
5052
OUTPUT_DIR=$(call quote,$(OUTPUT_DIR)) \
5153
INPUT_GLOBS=$(call quote,$(INPUT_GLOBS)) \
5254
MRBIND=$(call quote,$(MRBIND)) \
55+
MRBIND_FLAGS=$(call quote,$(MRBIND_FLAGS)) \
56+
MRBIND_FLAGS_FOR_EXTRA_INPUTS=$(call quote,$(MRBIND_FLAGS_FOR_EXTRA_INPUTS)) \
5357
COMPILER_FLAGS=$(call quote,$(COMPILER_FLAGS)) \
5458
COMPILER_FLAGS_LIBCLANG=$(call quote,$(COMPILER_FLAGS_LIBCLANG)) \
5559
COMPILER=$(call quote,$(COMPILER)) \
5660
LINKER_OUTPUT=$(call quote,$(LINKER_OUTPUT)) \
5761
LINKER=$(call quote,$(LINKER)) \
5862
LINKER_FLAGS=$(call quote,$(LINKER_FLAGS)) \
5963
NUM_FRAGMENTS=$(call quote,$(NUM_FRAGMENTS)) \
64+
EXTRA_INPUT_SOURCES=$(call quote,$(EXTRA_INPUT_SOURCES)) \
6065
)
6166

6267
# Generated mrmeshpy.
@@ -69,8 +74,8 @@ only-generate:
6974
$(MAKE) -f $(MRBIND_SOURCE)/scripts/apply_to_files.mk generate $(mrbind_vars)
7075

7176
# Handwritten mrmeshnumpy.
72-
MRMESHPY_MODULE := $(MODULE_OUTPUT_DIR)/mrmeshnumpy$(shell python3-config --extension-suffix)
73-
$(MRMESHPY_MODULE): | $(MODULE_OUTPUT_DIR)
77+
MRMESHNUMPY_MODULE := $(MODULE_OUTPUT_DIR)/mrmeshnumpy$(shell python3-config --extension-suffix)
78+
$(MRMESHNUMPY_MODULE): | $(MODULE_OUTPUT_DIR)
7479
$(CXX) \
7580
-o $@ \
7681
$(makefile_dir)/../../source/mrmeshnumpy/*.cpp \
@@ -82,7 +87,7 @@ $(MRMESHPY_MODULE): | $(MODULE_OUTPUT_DIR)
8287
# All modules.
8388
.DEFAULT_GOAL := all
8489
.PHONY: all
85-
all: $(LINKER_OUTPUT) $(MRMESHPY_MODULE)
90+
all: $(LINKER_OUTPUT) $(MRMESHNUMPY_MODULE)
8691

8792
# The directory for the modules.
8893
$(MODULE_OUTPUT_DIR):

scripts/mrbind/helpers.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include "MRMesh/MRBitSetParallelFor.h"
2+
#include "MRMesh/MRBoolean.h"
3+
#include "MRMesh/MREdgeIterator.h"
4+
#include "MRMesh/MRMesh.h"
5+
#include "MRMesh/MRPointsToMeshProjector.h"
6+
7+
// Only the functions that should be exported should be in `MR::Extra`. Place everything else somewhere outside.
8+
// Note that the comments are pasted to Python too.
9+
10+
namespace MR::Extra
11+
{
12+
// Fix self-intersections by converting to voxels and back.
13+
void fixSelfIntersections( Mesh& mesh, float voxelSize )
14+
{
15+
MeshVoxelsConverter convert;
16+
convert.voxelSize = voxelSize;
17+
auto gridA = convert(mesh);
18+
mesh = convert(gridA);
19+
}
20+
21+
// Subtract mesh B from mesh A.
22+
Mesh voxelBooleanSubtract( const Mesh& mesh1, const Mesh& mesh2, float voxelSize )
23+
{
24+
MeshVoxelsConverter convert;
25+
convert.voxelSize = voxelSize;
26+
auto gridA = convert(mesh1);
27+
auto gridB = convert(mesh2);
28+
gridA -= gridB;
29+
return convert(gridA);
30+
}
31+
32+
// Unite mesh A and mesh B.
33+
Mesh voxelBooleanUnite( const Mesh& mesh1, const Mesh& mesh2, float voxelSize )
34+
{
35+
MeshVoxelsConverter convert;
36+
convert.voxelSize = voxelSize;
37+
auto gridA = convert(mesh1);
38+
auto gridB = convert(mesh2);
39+
gridA += gridB;
40+
return convert( gridA );
41+
}
42+
43+
// Intersect mesh A and mesh B.
44+
Mesh voxelBooleanIntersect( const Mesh& mesh1, const Mesh& mesh2, float voxelSize )
45+
{
46+
MeshVoxelsConverter convert;
47+
convert.voxelSize = voxelSize;
48+
auto gridA = convert(mesh1);
49+
auto gridB = convert(mesh2);
50+
gridA *= gridB;
51+
return convert( gridA );
52+
}
53+
54+
// Computes signed distances from all mesh points to refMesh.
55+
// `refMesh` - all points will me projected to this mesh
56+
// `mesh` - this mesh points will be projected
57+
// `refXf` - world transform for refMesh
58+
// `upDistLimitSq` - upper limit on the distance in question, if the real distance is larger than the returning upDistLimit
59+
// `loDistLimitSq` - low limit on the distance in question, if a point is found within this distance then it is immediately returned without searching for a closer one
60+
VertScalars projectAllMeshVertices( const Mesh& refMesh, const Mesh& mesh, const AffineXf3f* refXf = nullptr, const AffineXf3f* xf = nullptr, float upDistLimitSq = FLT_MAX, float loDistLimitSq = 0.0f )
61+
{
62+
PointsToMeshProjector projector;
63+
projector.updateMeshData( &refMesh );
64+
std::vector<MeshProjectionResult> mpRes( mesh.points.vec_.size() );
65+
projector.findProjections( mpRes, mesh.points.vec_, xf, refXf, upDistLimitSq, loDistLimitSq );
66+
VertScalars res( mesh.topology.lastValidVert() + 1, std::sqrt( upDistLimitSq ) );
67+
68+
AffineXf3f fullXf;
69+
if ( refXf )
70+
fullXf = refXf->inverse();
71+
if ( xf )
72+
fullXf = fullXf * ( *xf );
73+
74+
BitSetParallelFor( mesh.topology.getValidVerts(), [&] ( VertId v )
75+
{
76+
const auto& mpResV = mpRes[v.get()];
77+
auto& resV = res[v];
78+
79+
resV = mpResV.distSq;
80+
if ( mpResV.mtp.e )
81+
resV = refMesh.signedDistance( fullXf( mesh.points[v] ), mpResV );
82+
else
83+
resV = std::sqrt( resV );
84+
} );
85+
return res;
86+
}
87+
88+
// Merge a list of meshes to one mesh.
89+
Mesh mergeMeshes( const std::vector<std::shared_ptr<MR::Mesh>>& meshes )
90+
{
91+
Mesh res;
92+
for ( const auto& m : meshes )
93+
res.addPart( *m );
94+
return res;
95+
}
96+
97+
// Return faces with at least one edge longer than the specified length.
98+
FaceBitSet getFacesByMinEdgeLength( const Mesh& mesh, float minLength )
99+
{
100+
using namespace MR;
101+
FaceBitSet resultFaces( mesh.topology.getValidFaces().size() );
102+
float minLengthSq = minLength * minLength;
103+
for ( auto ue : undirectedEdges( mesh.topology ) )
104+
{
105+
if ( mesh.edgeLengthSq( ue ) > minLengthSq )
106+
{
107+
auto l = mesh.topology.left( ue );
108+
auto r = mesh.topology.right( ue );
109+
if ( l )
110+
resultFaces.set( l );
111+
if ( r )
112+
resultFaces.set( r );
113+
}
114+
}
115+
return resultFaces;
116+
}
117+
}

scripts/mrbind/mrbind_flags.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
--ignore MR::detail
66
--ignore MR::Signal
77
--ignore MR::UniquePtr
8-
--ignore MR::OpenVdbFloatGrid
98
--ignore MR::RegisterRenderObjectConstructor
109
--ignore MR::Config
1110
--allow std::integral_constant
1211
--skip-base boost::dynamic_bitset
12+
--skip-base /openvdb::v[0-9][0-9_a-zA-Z]*::Grid/
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--format=macros
2+
--ignore-pch-flags
3+
--ignore ::
4+
--allow MR::Extra

source/MRMesh/MRBindingMacros.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
// Those macros help control Python bindings generated using MRBind.
4+
5+
// MR_PARSING_FOR_PB11_BINDINGS - gets defined when parsing the source code
6+
// MR_COMPILING_PB11_BINDINGS - gets defined when compiling the resulting bindings
7+
8+
// Use to specify valid template arguments for templates (usually function templates).
9+
// For example:
10+
// template <typename T> void foo(T) {...};
11+
// MR_BIND_TEMPLATE( void foo(int) )
12+
// MR_BIND_TEMPLATE( void foo(float) )
13+
//
14+
// As with `extern template`, you might need to use `foo<...>` instead of `foo` if the template parameters can't be deduced from the
15+
// parameter types and the return type.
16+
#ifdef MR_PARSING_FOR_PB11_BINDINGS
17+
#define MR_BIND_TEMPLATE(...) extern template __VA_ARGS__;
18+
#else
19+
#define MR_BIND_TEMPLATE(...)
20+
#endif

source/MRMesh/MRContour.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "MRBindingMacros.h"
34
#include "MRMeshFwd.h"
45
#include "MRVector3.h"
56

@@ -77,4 +78,14 @@ To copyContours( const From & from )
7778

7879
/// \}
7980

81+
// Instantiate the templates when generating bindings.
82+
MR_BIND_TEMPLATE( float calcOrientedArea( const Contour2<float> & contour ) )
83+
MR_BIND_TEMPLATE( double calcOrientedArea( const Contour2<double> & contour ) )
84+
MR_BIND_TEMPLATE( Vector3<float> calcOrientedArea( const Contour3<float> & contour ) )
85+
MR_BIND_TEMPLATE( Vector3<double> calcOrientedArea( const Contour3<double> & contour ) )
86+
MR_BIND_TEMPLATE( Contour2<float> copyContour( const Contour2<double> & from ) )
87+
MR_BIND_TEMPLATE( Contour3<float> copyContour( const Contour3<double> & from ) )
88+
MR_BIND_TEMPLATE( Contour2<double> copyContour( const Contour2<float> & from ) )
89+
MR_BIND_TEMPLATE( Contour3<double> copyContour( const Contour3<float> & from ) )
90+
8091
} // namespace MR

source/MRMesh/MRMesh.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@
418418
<ClInclude Include="MRMacros.h" />
419419
<ClInclude Include="MRTeethMaskToDirectionVolume.h" />
420420
<ClInclude Include="MRCanonicalTypedefs.h" />
421+
<ClInclude Include="MRBindingMacros.h" />
421422
</ItemGroup>
422423
<ItemGroup>
423424
<ClCompile Include="MR2DContoursTriangulation.cpp" />

source/MRMesh/MRMesh.vcxproj.filters

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,12 @@
13351335
<ClInclude Include="MRMeshAttributesToUpdate.h">
13361336
<Filter>Source Files\MeshAlgorithm</Filter>
13371337
</ClInclude>
1338-
<ClInclude Include="MRCanonicalTypedefs.h" />
1338+
<ClInclude Include="MRBindingMacros.h">
1339+
<Filter>Source Files\Basic</Filter>
1340+
</ClInclude>
1341+
<ClInclude Include="MRCanonicalTypedefs.h">
1342+
<Filter>Source Files\Basic</Filter>
1343+
</ClInclude>
13391344
</ItemGroup>
13401345
<ItemGroup>
13411346
<ClCompile Include="MRObject.cpp">
@@ -2226,4 +2231,4 @@
22262231
<Filter>Source Files\Python</Filter>
22272232
</CopyFileToFolders>
22282233
</ItemGroup>
2229-
</Project>
2234+
</Project>

test_python/test_voxels_conversion.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,18 @@ def test_voxels_conversion():
3636

3737
# Test mesh build functions
3838
radius = 8 * mul
39-
mesh1 = mrmesh.gridToMesh(vdb_volume, isoValue = max_value - radius * radius)
40-
mesh2 = mrmesh.gridToMesh(grid, voxelSize = volume0.voxelSize, isoValue = max_value - radius * radius)
39+
if is_new_binding:
40+
settings1 = mrmesh.GridToMeshSettings()
41+
settings1.isoValue = max_value - radius * radius
42+
settings1.voxelSize = vdb_volume.voxelSize
43+
mesh1 = mrmesh.gridToMesh(vdb_volume.data, settings1)
44+
settings2 = mrmesh.GridToMeshSettings()
45+
settings2.isoValue = max_value - radius * radius
46+
settings2.voxelSize = volume0.voxelSize
47+
mesh2 = mrmesh.gridToMesh(grid, settings2)
48+
else:
49+
mesh1 = mrmesh.gridToMesh(vdb_volume, isoValue = max_value - radius * radius)
50+
mesh2 = mrmesh.gridToMesh(grid, voxelSize = volume0.voxelSize, isoValue = max_value - radius * radius)
4151
# Basic checks
4252
for mesh in (mesh1, mesh2):
4353
assert len(mrmesh.getAllComponents(mesh1)) == 1

0 commit comments

Comments
 (0)