Initial commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>18.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{edbc9fb5-7482-46d2-bc85-915e34fb1e97}</ProjectGuid>
|
||||
<RootNamespace>VerifyCpp</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<RepositoryRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\'))</RepositoryRoot>
|
||||
<OutDir>$(RepositoryRoot)build\msbuild\bin\$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(RepositoryRoot)build\msbuild\obj\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<LocalDebuggerWorkingDirectory>$(RepositoryRoot)</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="verify_cpp.cpp" />
|
||||
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\plane.h" />
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\precise_geometry.h" />
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\util.h" />
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\wide_real.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{ace0167e-2ac0-47cd-a825-5f520d30f8aa}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{bc3bd4ce-bb85-4b5d-bc63-a16e38f8c04f}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="verify_cpp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\plane.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\precise_geometry.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\NeighborlyCore\wide_real.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,496 @@
|
||||
#include "util.h"
|
||||
#include "precise_geometry.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Crossing {
|
||||
int face = -1;
|
||||
Edge edge_a;
|
||||
Edge edge_b;
|
||||
double t = 0.0;
|
||||
double u = 0.0;
|
||||
};
|
||||
|
||||
struct EdgeFaceIntersection {
|
||||
Edge edge;
|
||||
int face = -1;
|
||||
double t = 0.0;
|
||||
bool on_boundary = false;
|
||||
};
|
||||
|
||||
struct PlanarityResidual {
|
||||
int face = -1;
|
||||
double max_abs = 0.0;
|
||||
double rms = 0.0;
|
||||
};
|
||||
|
||||
struct Verification {
|
||||
std::string obj_path;
|
||||
double eps = 1e-12;
|
||||
int topology = 4;
|
||||
size_t vertices = 0;
|
||||
size_t faces = 0;
|
||||
size_t edges = 0;
|
||||
int euler = 0;
|
||||
int genus = 0;
|
||||
std::vector<size_t> face_sizes;
|
||||
std::vector<Edge> unique_edges;
|
||||
std::vector<Edge> bad_edge_incidence;
|
||||
std::vector<std::pair<int, int>> bad_face_pairs;
|
||||
std::vector<Crossing> crossings;
|
||||
std::vector<EdgeFaceIntersection> intersections;
|
||||
std::vector<PlanarityResidual> planarity;
|
||||
int original_crossings = 0;
|
||||
int original_intersections = 0;
|
||||
bool finite = false;
|
||||
double geometry_scale = 0.0;
|
||||
double min_edge_length = 0.0;
|
||||
double max_planarity_residual = 0.0;
|
||||
};
|
||||
|
||||
Edge canonical_edge(int a, int b) {
|
||||
return a < b ? Edge(a, b) : Edge(b, a);
|
||||
}
|
||||
|
||||
std::string edge_obj_string(const Edge& edge) {
|
||||
std::ostringstream out;
|
||||
out << "[" << edge.first + 1 << "," << edge.second + 1 << "]";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bool segment_intersection_2d(
|
||||
const Vector2d& a,
|
||||
const Vector2d& b,
|
||||
const Vector2d& c,
|
||||
const Vector2d& d,
|
||||
double eps,
|
||||
double& t,
|
||||
double& u
|
||||
) {
|
||||
return precise_segment_intersection_2d(a, b, c, d, eps, &t, &u);
|
||||
}
|
||||
|
||||
std::map<Edge, std::vector<int>> make_edge_to_faces(const Faces& faces) {
|
||||
std::map<Edge, std::vector<int>> result;
|
||||
for (size_t face_ix = 0; face_ix < faces.size(); ++face_ix) {
|
||||
const Face& face = faces[face_ix];
|
||||
for (size_t i = 0; i < face.size(); ++i) {
|
||||
result[canonical_edge(face[i], face[(i + 1) % face.size()])].push_back((int)face_ix);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Crossing> find_self_crossings(const Verts3D& verts, const Faces& faces, const Planes& planes, double eps) {
|
||||
std::vector<Crossing> result;
|
||||
for (size_t face_ix = 0; face_ix < faces.size(); ++face_ix) {
|
||||
const Face& face = faces[face_ix];
|
||||
Verts2D projected;
|
||||
make_2d_projection(verts, face, planes[face_ix], projected);
|
||||
|
||||
const size_t n = face.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const size_t i_next = (i + 1) % n;
|
||||
for (size_t j = i + 1; j < n; ++j) {
|
||||
const size_t j_next = (j + 1) % n;
|
||||
if (i_next == j || j_next == i) {
|
||||
continue;
|
||||
}
|
||||
std::set<int> vertices = {face[i], face[i_next], face[j], face[j_next]};
|
||||
if (vertices.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double t = 0.0;
|
||||
double u = 0.0;
|
||||
if (segment_intersection_2d(projected[i], projected[i_next], projected[j], projected[j_next], eps, t, u)) {
|
||||
Crossing crossing;
|
||||
crossing.face = (int)face_ix;
|
||||
crossing.edge_a = Edge(face[i], face[i_next]);
|
||||
crossing.edge_b = Edge(face[j], face[j_next]);
|
||||
crossing.t = t;
|
||||
crossing.u = u;
|
||||
result.push_back(crossing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<EdgeFaceIntersection> find_edge_face_intersections(
|
||||
const Verts3D& verts,
|
||||
const Faces& faces,
|
||||
const Planes& planes,
|
||||
const std::vector<Edge>& edges,
|
||||
double eps
|
||||
) {
|
||||
std::vector<EdgeFaceIntersection> result;
|
||||
std::vector<std::set<int>> face_vertices;
|
||||
face_vertices.reserve(faces.size());
|
||||
for (const Face& face : faces) {
|
||||
face_vertices.emplace_back(face.begin(), face.end());
|
||||
}
|
||||
|
||||
for (const Edge& edge : edges) {
|
||||
const int a_ix = edge.first;
|
||||
const int b_ix = edge.second;
|
||||
const Vector3d a = verts[a_ix];
|
||||
const Vector3d b = verts[b_ix];
|
||||
|
||||
for (size_t face_ix = 0; face_ix < faces.size(); ++face_ix) {
|
||||
if (face_vertices[face_ix].count(a_ix) || face_vertices[face_ix].count(b_ix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Plane& plane = planes[face_ix];
|
||||
WideReal precise_t;
|
||||
if (!precise_edge_plane_parameter(a, b, plane, eps, precise_t)) {
|
||||
continue;
|
||||
}
|
||||
const double t = precise_t.to_double();
|
||||
|
||||
Matrix3d basis;
|
||||
Vector3d origin;
|
||||
Verts2D projected;
|
||||
make_2d_projection(verts, faces[face_ix], plane, projected, basis, origin);
|
||||
const WidePoint2 hit2 = precise_projected_edge_hit(a, b, precise_t, basis, origin);
|
||||
bool on_boundary = false;
|
||||
if (precise_point_in_polygon_2d(hit2, projected, eps, &on_boundary)) {
|
||||
EdgeFaceIntersection hit;
|
||||
hit.edge = edge;
|
||||
hit.face = (int)face_ix;
|
||||
hit.t = t;
|
||||
hit.on_boundary = on_boundary;
|
||||
result.push_back(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<PlanarityResidual> measure_planarity(const Verts3D& verts, const Faces& faces, const Planes& planes) {
|
||||
std::vector<PlanarityResidual> result;
|
||||
for (size_t face_ix = 0; face_ix < faces.size(); ++face_ix) {
|
||||
const Face& face = faces[face_ix];
|
||||
double max_abs = 0.0;
|
||||
double sum_sq = 0.0;
|
||||
for (int vertex_ix : face) {
|
||||
const double distance = planes[face_ix].signed_distance(verts[vertex_ix]);
|
||||
max_abs = std::max(max_abs, std::abs(distance));
|
||||
sum_sq += double(distance) * double(distance);
|
||||
}
|
||||
PlanarityResidual item;
|
||||
item.face = (int)face_ix;
|
||||
item.max_abs = max_abs;
|
||||
item.rms = (double)std::sqrt(sum_sq / double(face.size()));
|
||||
result.push_back(item);
|
||||
}
|
||||
std::sort(result.begin(), result.end(), [](const PlanarityResidual& a, const PlanarityResidual& b) {
|
||||
return a.max_abs > b.max_abs;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Verification verify_obj(const std::string& obj_path, int topology, double eps) {
|
||||
Verification verification;
|
||||
verification.obj_path = obj_path;
|
||||
verification.topology = topology;
|
||||
verification.eps = eps;
|
||||
|
||||
g_topology = topology;
|
||||
Verts3D verts;
|
||||
Planes planes;
|
||||
import_obj(obj_path.c_str(), verts, g_polys);
|
||||
verification.finite = is_finite(verts);
|
||||
if (verts.empty() || g_polys.empty()) {
|
||||
verification.finite = false;
|
||||
return verification;
|
||||
}
|
||||
make_edges(g_polys, g_edges);
|
||||
v3ds_to_planes(verts, g_polys, planes);
|
||||
|
||||
const std::map<Edge, std::vector<int>> edge_to_faces = make_edge_to_faces(g_polys);
|
||||
for (const auto& item : edge_to_faces) {
|
||||
verification.unique_edges.push_back(item.first);
|
||||
if (item.second.size() != 2) {
|
||||
verification.bad_edge_incidence.push_back(item.first);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::set<Edge>> face_edge_sets;
|
||||
for (const Face& face : g_polys) {
|
||||
std::set<Edge> face_edges;
|
||||
for (size_t i = 0; i < face.size(); ++i) {
|
||||
face_edges.insert(canonical_edge(face[i], face[(i + 1) % face.size()]));
|
||||
}
|
||||
face_edge_sets.push_back(face_edges);
|
||||
}
|
||||
for (size_t i = 0; i < face_edge_sets.size(); ++i) {
|
||||
for (size_t j = i + 1; j < face_edge_sets.size(); ++j) {
|
||||
std::vector<Edge> shared;
|
||||
std::set_intersection(
|
||||
face_edge_sets[i].begin(),
|
||||
face_edge_sets[i].end(),
|
||||
face_edge_sets[j].begin(),
|
||||
face_edge_sets[j].end(),
|
||||
std::back_inserter(shared)
|
||||
);
|
||||
if (shared.size() != 1) {
|
||||
verification.bad_face_pairs.emplace_back((int)i, (int)j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verification.vertices = verts.size();
|
||||
verification.faces = g_polys.size();
|
||||
verification.edges = verification.unique_edges.size();
|
||||
verification.euler = (int)verification.vertices - (int)verification.edges + (int)verification.faces;
|
||||
verification.genus = 1 - verification.euler / 2;
|
||||
for (const Face& face : g_polys) {
|
||||
verification.face_sizes.push_back(face.size());
|
||||
}
|
||||
|
||||
verification.crossings = find_self_crossings(verts, g_polys, planes, eps);
|
||||
verification.intersections = find_edge_face_intersections(verts, g_polys, planes, verification.unique_edges, eps);
|
||||
verification.planarity = measure_planarity(verts, g_polys, planes);
|
||||
std::vector<double> edge_lengths;
|
||||
edge_lengths.reserve(verification.unique_edges.size());
|
||||
for (const Edge& edge : verification.unique_edges) {
|
||||
edge_lengths.push_back((verts[edge.first] - verts[edge.second]).norm());
|
||||
}
|
||||
if (!edge_lengths.empty()) {
|
||||
std::sort(edge_lengths.begin(), edge_lengths.end());
|
||||
verification.min_edge_length = edge_lengths.front();
|
||||
verification.geometry_scale = edge_lengths[edge_lengths.size() / 2];
|
||||
}
|
||||
if (!verification.planarity.empty()) {
|
||||
verification.max_planarity_residual = verification.planarity.front().max_abs;
|
||||
}
|
||||
verification.original_crossings = count_crossings(verts, planes);
|
||||
verification.original_intersections = count_intersections(verts, planes);
|
||||
return verification;
|
||||
}
|
||||
|
||||
bool has_all_11_gons(const Verification& verification) {
|
||||
return std::all_of(verification.face_sizes.begin(), verification.face_sizes.end(), [](size_t size) {
|
||||
return size == 11;
|
||||
});
|
||||
}
|
||||
|
||||
bool is_ok(const Verification& verification) {
|
||||
return verification.vertices == 44
|
||||
&& verification.faces == 12
|
||||
&& verification.edges == 66
|
||||
&& has_all_11_gons(verification)
|
||||
&& verification.bad_face_pairs.empty()
|
||||
&& verification.bad_edge_incidence.empty()
|
||||
&& verification.genus == 6
|
||||
&& verification.finite
|
||||
&& verification.geometry_scale > 0.0
|
||||
&& verification.min_edge_length > verification.geometry_scale * 1e-8
|
||||
&& verification.max_planarity_residual <= verification.geometry_scale * 1e-4
|
||||
&& verification.crossings.empty()
|
||||
&& verification.intersections.empty();
|
||||
}
|
||||
|
||||
std::string render_markdown(const Verification& verification) {
|
||||
std::ostringstream out;
|
||||
out << "# C++ high-precision verification\n\n";
|
||||
out << "- Source: `" << verification.obj_path << "`\n";
|
||||
out << "- Topology: `" << verification.topology << "`\n";
|
||||
out << "- Epsilon: `" << std::scientific << std::setprecision(17)
|
||||
<< verification.eps << std::defaultfloat << "`\n";
|
||||
out << "- Predicate arithmetic: double-double (`~" << WideReal::decimal_digits << " decimal digits`)\n";
|
||||
out << "- Overall: **" << (is_ok(verification) ? "OK" : "FAIL") << "**\n\n";
|
||||
|
||||
out << "## Combinatorics\n\n";
|
||||
out << "- V/E/F: `" << verification.vertices << "/" << verification.edges << "/" << verification.faces << "`\n";
|
||||
out << "- Euler characteristic: `" << verification.euler << "`\n";
|
||||
out << "- Genus: `" << verification.genus << "`\n";
|
||||
out << "- All faces are 11-gons: `" << (has_all_11_gons(verification) ? "yes" : "no") << "`\n";
|
||||
out << "- Face-pair failures: `" << verification.bad_face_pairs.size() << "`\n";
|
||||
out << "- Edge incidence failures: `" << verification.bad_edge_incidence.size() << "`\n\n";
|
||||
|
||||
out << "## Geometry\n\n";
|
||||
out << "- Finite coordinates: `" << (verification.finite ? "yes" : "no") << "`\n";
|
||||
out << "- Median/min edge length: `" << verification.geometry_scale << "/"
|
||||
<< verification.min_edge_length << "`\n";
|
||||
out << "- Max planarity residual: `" << verification.max_planarity_residual << "`\n";
|
||||
out << "- Strict C++ self-crossings: `" << verification.crossings.size() << "`\n";
|
||||
out << "- Strict C++ edge-face intersections: `" << verification.intersections.size() << "`\n";
|
||||
out << "- Original count_crossings: `" << verification.original_crossings << "`\n";
|
||||
out << "- Original count_intersections: `" << verification.original_intersections << "`\n\n";
|
||||
|
||||
out << "## Self-crossings\n\n";
|
||||
if (verification.crossings.empty()) {
|
||||
out << "- None\n";
|
||||
} else {
|
||||
for (const Crossing& crossing : verification.crossings) {
|
||||
out << "- Face " << crossing.face << " (OBJ face " << crossing.face + 1 << "): edge "
|
||||
<< edge_obj_string(crossing.edge_a) << " crosses edge " << edge_obj_string(crossing.edge_b)
|
||||
<< " (t=" << std::setprecision(17) << crossing.t << ", u=" << crossing.u << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
out << "\n## Edge-face intersections\n\n";
|
||||
if (verification.intersections.empty()) {
|
||||
out << "- None\n";
|
||||
} else {
|
||||
for (const EdgeFaceIntersection& hit : verification.intersections) {
|
||||
out << "- Edge " << edge_obj_string(hit.edge) << " intersects OBJ face " << hit.face + 1
|
||||
<< " (t=" << std::setprecision(17) << hit.t
|
||||
<< ", " << (hit.on_boundary ? "boundary" : "interior") << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
out << "\n## Worst face planarity residuals\n\n";
|
||||
const size_t residual_count = std::min<size_t>(5, verification.planarity.size());
|
||||
for (size_t i = 0; i < residual_count; ++i) {
|
||||
const PlanarityResidual& residual = verification.planarity[i];
|
||||
out << "- Face " << residual.face << " (OBJ face " << residual.face + 1 << "): max `"
|
||||
<< std::setprecision(17) << residual.max_abs << "`, rms `" << residual.rms << "`\n";
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string json_escape(const std::string& value) {
|
||||
std::ostringstream out;
|
||||
for (const char ch : value) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
out << "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
out << "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
out << "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
out << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
out << "\\t";
|
||||
break;
|
||||
default:
|
||||
out << ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string render_json(const Verification& verification) {
|
||||
std::ostringstream out;
|
||||
out << "{\n";
|
||||
out << " \"source\": \"" << json_escape(verification.obj_path) << "\",\n";
|
||||
out << " \"topology\": " << verification.topology << ",\n";
|
||||
out << " \"eps\": " << std::setprecision(17) << verification.eps << ",\n";
|
||||
out << " \"predicate_decimal_digits\": " << WideReal::decimal_digits << ",\n";
|
||||
out << " \"vertices\": " << verification.vertices << ",\n";
|
||||
out << " \"edges\": " << verification.edges << ",\n";
|
||||
out << " \"faces\": " << verification.faces << ",\n";
|
||||
out << " \"euler\": " << verification.euler << ",\n";
|
||||
out << " \"genus\": " << verification.genus << ",\n";
|
||||
out << " \"finite\": " << (verification.finite ? "true" : "false") << ",\n";
|
||||
out << " \"geometry_scale\": " << verification.geometry_scale << ",\n";
|
||||
out << " \"min_edge_length\": " << verification.min_edge_length << ",\n";
|
||||
out << " \"max_planarity_residual\": " << verification.max_planarity_residual << ",\n";
|
||||
out << " \"strict_self_crossings\": " << verification.crossings.size() << ",\n";
|
||||
out << " \"strict_edge_face_intersections\": " << verification.intersections.size() << ",\n";
|
||||
out << " \"original_count_crossings\": " << verification.original_crossings << ",\n";
|
||||
out << " \"original_count_intersections\": " << verification.original_intersections << ",\n";
|
||||
out << " \"ok\": " << (is_ok(verification) ? "true" : "false") << ",\n";
|
||||
out << " \"self_crossings\": [\n";
|
||||
for (size_t i = 0; i < verification.crossings.size(); ++i) {
|
||||
const Crossing& crossing = verification.crossings[i];
|
||||
out << " {\"face0\": " << crossing.face
|
||||
<< ", \"face1\": " << crossing.face + 1
|
||||
<< ", \"edge_a_obj\": [" << crossing.edge_a.first + 1 << ", " << crossing.edge_a.second + 1 << "]"
|
||||
<< ", \"edge_b_obj\": [" << crossing.edge_b.first + 1 << ", " << crossing.edge_b.second + 1 << "]"
|
||||
<< ", \"t\": " << crossing.t
|
||||
<< ", \"u\": " << crossing.u << "}";
|
||||
out << (i + 1 == verification.crossings.size() ? "\n" : ",\n");
|
||||
}
|
||||
out << " ],\n";
|
||||
out << " \"edge_face_intersections\": [\n";
|
||||
for (size_t i = 0; i < verification.intersections.size(); ++i) {
|
||||
const EdgeFaceIntersection& hit = verification.intersections[i];
|
||||
out << " {\"edge_obj\": [" << hit.edge.first + 1 << ", " << hit.edge.second + 1 << "]"
|
||||
<< ", \"face0\": " << hit.face
|
||||
<< ", \"face1\": " << hit.face + 1
|
||||
<< ", \"t\": " << hit.t
|
||||
<< ", \"on_boundary\": " << (hit.on_boundary ? "true" : "false") << "}";
|
||||
out << (i + 1 == verification.intersections.size() ? "\n" : ",\n");
|
||||
}
|
||||
out << " ]\n";
|
||||
out << "}\n";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void write_text_file(const std::string& path, const std::string& contents) {
|
||||
if (path.empty()) {
|
||||
return;
|
||||
}
|
||||
const std::filesystem::path output_path(path);
|
||||
if (output_path.has_parent_path()) {
|
||||
std::filesystem::create_directories(output_path.parent_path());
|
||||
}
|
||||
std::ofstream out(path);
|
||||
out << contents;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (!wide_real_self_test()) {
|
||||
std::cerr << "WideReal self-test failed.\n";
|
||||
return 3;
|
||||
}
|
||||
std::string obj_path = "data/shape_c2_i0_0.obj";
|
||||
std::string report_path = "runtime/reports/00_baseline.md";
|
||||
std::string json_path = "runtime/reports/baseline.json";
|
||||
int topology = 4;
|
||||
double eps = 1e-12;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string arg = argv[i];
|
||||
if ((arg == "--obj" || arg == "-o") && i + 1 < argc) {
|
||||
obj_path = argv[++i];
|
||||
} else if ((arg == "--topology" || arg == "-t") && i + 1 < argc) {
|
||||
topology = std::atoi(argv[++i]);
|
||||
} else if (arg == "--eps" && i + 1 < argc) {
|
||||
eps = (double)std::atof(argv[++i]);
|
||||
} else if (arg == "--report" && i + 1 < argc) {
|
||||
report_path = argv[++i];
|
||||
} else if (arg == "--json" && i + 1 < argc) {
|
||||
json_path = argv[++i];
|
||||
} else {
|
||||
std::cerr << "Usage: verify_cpp [--obj path] [--topology n] [--eps value] [--report path] [--json path]\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const Verification verification = verify_obj(obj_path, topology, eps);
|
||||
const std::string markdown = render_markdown(verification);
|
||||
const std::string json = render_json(verification);
|
||||
write_text_file(report_path, markdown);
|
||||
write_text_file(json_path, json);
|
||||
|
||||
std::cout << markdown;
|
||||
return is_ok(verification) ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user