Compare commits

...

12 Commits

Author SHA1 Message Date
mckay 2f096108cf fix: refactor hash operator for cylinder and plane 5 months ago
mckay 31daca7cf2 style: add comments and tests 5 months ago
mckay ff69eb0a58 fix: update hasher to use world_to_local matrix instead of local_to_world 6 months ago
mckay 022a8b67b5 z 6 months ago
mckay aaf8565e96 refactor(cylinder_face): use geometric hash based on world_to_local transform 6 months ago
mckay c139aadb9c fix: replace local_to_world matrix usage with fetch_aabb in mark_primitive_boundings 6 months ago
mckay df7f80a350 fix: refactor translation handling in apply_transform to use Eigen::Translation3d 6 months ago
mckay 3a97488f8e Revert "fix: Make get_identity_local_to_world virtual to support primitives with multiple subfaces (e.g., cylinder)." 6 months ago
mckay 753adc4bf4 fix: update matrix references in hasher for cylinder and plane to use local_to_world. Hash of plane changed to col 0 and 3 6 months ago
mckay f0e2d2b204 fix: Make get_identity_local_to_world virtual to support primitives with multiple subfaces (e.g., cylinder). 6 months ago
mckay 2c5729946b Revert "fix: enhance logging in apply_transform for scale and translation operations" 6 months ago
mckay 82ade709c7 fix: enhance logging in apply_transform for scale and translation operations 6 months ago
  1. 23
      .vscode/settings.json
  2. 13
      application/main.cpp
  3. 3
      network_process/src/prim_gen/extract_vertex_infos.cpp
  4. 10
      primitive_process/interface/base/primitive.hpp
  5. 36
      primitive_process/interface/subface/simple/cylinder_face.hpp
  6. 27
      primitive_process/interface/subface/simple/plane.hpp
  7. 21
      primitive_process/src/base/primitive.cpp
  8. 7
      primitive_process/src/base/subface.cpp
  9. 267
      primitive_process/test/test_equivalence.cpp
  10. 9
      primitive_process/xmake.lua
  11. 3
      shared_module/container/hashed_refcount_hive.hpp

23
.vscode/settings.json

@ -0,0 +1,23 @@
{
"files.associations": {
"vector": "cpp",
"type_traits": "cpp",
"stdexcept": "cpp",
"array": "cpp",
"bitset": "cpp",
"deque": "cpp",
"initializer_list": "cpp",
"list": "cpp",
"queue": "cpp",
"stack": "cpp",
"utility": "cpp",
"xhash": "cpp",
"xstring": "cpp",
"xutility": "cpp",
"memory": "cpp",
"mutex": "cpp",
"shared_mutex": "cpp",
"chrono": "cpp",
"functional": "cpp"
}
}

13
application/main.cpp

@ -36,14 +36,25 @@ int main()
auto sphere1 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_SPHERE);
// auto sphere2 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_SPHERE);
auto cylinder = create_primitive(primitive_data_center, PRIMITIVE_TYPE_CYLINDER);
auto cylinder2 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_CYLINDER);
//primitive_apply_translation(sphere1, {.0, 0.0, 0.0});
primitive_apply_scale(cylinder, {1, 1, 2});
primitive_apply_translation(cylinder, {0.5, 0.0, 0.0});
primitive_apply_translation(cylinder2, {0.5, 0.0, 1.0});
//primitive_apply_rotation(cylinder, {0.9659258, 0.0, 0.0, -0.2588190});
//primitive_apply_rotation(cylinder2, {0.9659258, 0.0, 0.0, -0.2588190});
//primitive_apply_scale(cylinder, {1, 1, 2});
//primitive_apply_scale(sphere1, {1, 1, 2});
//primitive_apply_scale(cylinder, {1, 1, 2});
std::cout << "primitive created..." << std::endl;
auto runtime_blobtree = create_blobtree();
auto node_iter1 = blobtree_add_primitive_node(runtime_blobtree, cylinder);
auto node_iter2 = blobtree_add_primitive_node(runtime_blobtree, sphere1);
auto node_iter3 = blobtree_add_operation_node(runtime_blobtree, node_iter1, node_iter2, INTERSECTION_OP);
auto node_iter4 = blobtree_add_primitive_node(runtime_blobtree, cylinder2);
auto node_iter5 = blobtree_add_operation_node(runtime_blobtree, node_iter3, node_iter4, UNION_OP);
auto baked_blobtree = bake_blobtree(runtime_blobtree);
destroy_blobtree(runtime_blobtree);

3
network_process/src/prim_gen/extract_vertex_infos.cpp

@ -10,8 +10,7 @@ aabb_t mark_primitive_boundings(double aabb_margin, const baked_blobtree_t& tree
aabb_t scene_aabb{};
for (const auto [primitive_ptr, _] : tree.primitives) {
const auto& primitive = *primitive_ptr;
auto local_to_world_matrix = primitive.get_identity_local_to_world();
aabb_t primitive_aabb = aabb_unit.transformed(local_to_world_matrix);
aabb_t primitive_aabb = primitive.fetch_aabb();
primitive_aabb.min().array() -= aabb_margin;
primitive_aabb.max().array() += aabb_margin;
scene_aabb.extend(primitive_aabb);

10
primitive_process/interface/base/primitive.hpp

@ -12,6 +12,7 @@
template <typename T>
using marked_subface_ptr_t = marked_ptr<T, 1>;
using aabb_t = Eigen::AlignedBox<double, 3>;
enum class sign_t : uint8_t { positive = 1, negative = 2, zero = 0 };
@ -52,17 +53,14 @@ EXTERN_C struct PE_API primitive {
virtual primitive_type get_type() const = 0;
virtual marked_subface_ptr_t<subface> *get_subface() const = 0;
virtual size_t get_subface_count() const = 0;
aabb_t m_aabb = aabb_t(Eigen::Vector3d(-1, -1, -1), Eigen::Vector3d(1, 1, 1));
// sign_t judge_sign_by_subface_sdf(const std::vector<double> &) const;
// sign_t judge_sign_by_subface_sdf_sign(const std::vector<sign_t> &) const;
dynamic_bitset_mp<> judge_sign_by_subface_sign(stl_vector_mp<dynamic_bitset_mp<>>) const;
// for simple primitive: sphere, cylinder, cone, box
// we use world_to_local as OBB and judge inside/outside by [-1, 1]^3
// so we don't need to fetch aabb anymore
// aabb fetch_aabb() const;
// CAUTION: keep characteristics local_to_world as the first subface's part
const internal::transform_block &get_identity_local_to_world() const;
aabb_t fetch_aabb() const;
void apply_transform(internal::transform_type, Eigen::Vector4d);

36
primitive_process/interface/subface/simple/cylinder_face.hpp

@ -34,8 +34,40 @@ template <>
struct hasher<internal::cylinder_paired_model_matrix> {
size_t operator()(const internal::cylinder_paired_model_matrix &block) const
{
Eigen::Matrix<double, 2, 4> character_rows = block.data->world_to_local.matrix().topRows<2>();
return XXH3_64bits(character_rows.data(), sizeof(decltype(character_rows)));
// 使用 world_to_local 矩阵(4x4 齐次变换)
const auto& mat = block.data->world_to_local.matrix(); // 4x4
// 提取线性部分 A (3x3) 和平移部分 b (3x1)
Eigen::Matrix3d A = mat.block<3,3>(0,0);
Eigen::Vector3d b = mat.block<3,1>(0,3);
// ✅ 直接使用 A 的前两行作为 R(不需要逆)
// 因为 A 就是 world_to_local 的线性部分,row(0)=x, row(1)=y
Eigen::Matrix<double, 2, 3> R;
R.row(0) = A.row(0);
R.row(1) = A.row(1);
// ✅ 归一化 R 的行向量(消除缩放影响)
double norm0 = R.row(0).norm();
double norm1 = R.row(1).norm();
if (norm0 > 1e-8) R.row(0) /= norm0;
if (norm1 > 1e-8) R.row(1) /= norm1;
// G = R^T * R 编码横截面方向(已归一化,对旋转/缩放鲁棒)
Eigen::Matrix3d G = R.transpose() * R;
// ❌ 原始 R*b 依赖局部坐标系旋转
// ✅ 改为:只使用 ||R*b||^2(旋转不变)或投影长度平方
Eigen::Vector2d rp = R * b;
double radial_proj_sq = rp.squaredNorm(); // 旋转不变量
// 构造哈希键:G (3x3) + radial_proj_sq (1 double)
// 使用紧凑方式避免结构体
Eigen::Matrix<double, 10, 1> hash_key;
hash_key.head<9>() = Eigen::Map<const Eigen::Matrix<double, 9, 1>>(G.data());
hash_key(9) = radial_proj_sq;
return XXH3_64bits(hash_key.data(), sizeof(double) * 10);
}
};

27
primitive_process/interface/subface/simple/plane.hpp

@ -34,8 +34,31 @@ template <>
struct hasher<internal::plane_paired_model_matrix> {
size_t operator()(const internal::plane_paired_model_matrix &block) const
{
Eigen::Vector4d character_row = block.data->world_to_local.matrix().row(0);
return XXH3_64bits(character_row.data(), sizeof(Eigen::Vector4d));
const auto& mat = block.data->local_to_world.matrix();
Eigen::Vector3d n_vec = mat.col(0);
double tz = mat.col(3).transpose().dot(n_vec);
double norm = n_vec.norm();
if (norm < 1e-8) {
n_vec = Eigen::Vector3d::UnitZ();
norm = 1.0;
} else {
n_vec /= norm;
}
double d = -tz / norm; // 平面方程: n·x = d
// 可选:统一法向方向(例如让 z 分量优先为正)
if (n_vec[2] < -1e-8 ||
(std::abs(n_vec[2]) < 1e-8 && (n_vec[1] < -1e-8 ||
(std::abs(n_vec[1]) < 1e-8 && n_vec[0] < 0)))) {
n_vec = -n_vec;
d = -d;
}
Eigen::Vector4d hash_key;
hash_key << n_vec, d;
return XXH3_64bits(hash_key.data(), sizeof(Eigen::Vector4d));
}
};

21
primitive_process/src/base/primitive.cpp

@ -42,9 +42,9 @@ dynamic_bitset_mp<> primitive::judge_sign_by_subface_sign(stl_vector_mp<dynamic_
return res;
}
const internal::transform_block &primitive::get_identity_local_to_world() const
aabb_t primitive::fetch_aabb() const
{
return get_subface()[0].get_ptr()->raw_local_to_world();
return m_aabb;
}
void primitive::apply_transform(internal::transform_type type, Eigen::Vector4d param)
@ -59,5 +59,22 @@ void primitive::apply_transform(internal::transform_type type, Eigen::Vector4d p
new_model_matrices.emplace_back(subface_ptr->apply_transform(type, param));
}
// aabb
Eigen::Affine3d affine = Eigen::Affine3d::Identity();
switch (type) {
case internal::transform_type::scale: {
affine.linear() = param.head<3>().asDiagonal();
} break;
case internal::transform_type::rotation: {
affine.linear() = Eigen::Quaterniond(param).toRotationMatrix();
} break;
case internal::transform_type::translation: {
Eigen::Translation3d translation(param.head<3>());
affine = Eigen::Affine3d(translation);
} break;
default: throw std::invalid_argument("Invalid transform type");
}
m_aabb = m_aabb.transformed(affine);
initialize(*subfaces[0].get_ptr()->data_center, new_model_matrices);
}

7
primitive_process/src/base/subface.cpp

@ -31,8 +31,9 @@ std::pair<internal::paired_model_matrix *, bool> subface::apply_transform(intern
temp.world_to_local.linear() = temp.world_to_local.linear() * rotation_matrix.transpose();
} break;
case internal::transform_type::translation: {
temp.local_to_world.translation() += param.head<3>();
temp.world_to_local.translation() -= param.head<3>();
Eigen::Translation3d translation(param.head<3>());
temp.local_to_world = translation * temp.local_to_world;
temp.world_to_local = temp.world_to_local * translation.inverse();
} break;
default: throw std::invalid_argument("Invalid transform type");
}
@ -43,7 +44,7 @@ std::pair<internal::paired_model_matrix *, bool> subface::apply_transform(intern
temp.world_to_local.matrix() *= -1;
reversed = true;
}
std::cout<< "type: " << "unknow" << std::endl;
auto [iter, _] = data_center->transform_blocks.acquire(temp);
return {iter.operator->(), reversed};
}

267
primitive_process/test/test_equivalence.cpp

@ -0,0 +1,267 @@
#include "cylinder_face.hpp"
#include <Eigen/Dense>
#include <cassert>
#include <iostream>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// 工具函数:打印分隔线
void print_separator() {
std::cout << "\n" << std::string(60, '-') << "\n" << std::endl;
}
// 工具函数:打印测试标题
void print_test_title(const std::string& title) {
std::cout << "\033[1;36m"; // 浅蓝色
std::cout << title << "\033[0m" << std::endl;
}
// 工具函数:打印哈希值
void print_hash(size_t h, const std::string& label) {
std::cout << " " << label << " Hash = " << h << std::endl;
}
// 工具函数:断言并输出通过/失败
void assert_equal_hashes(size_t h1, size_t h2, const std::string& msg) {
if (h1 == h2) {
std::cout << " \033[32mPASSED\033[0m: " << msg << std::endl;
} else {
std::cout << " \033[31mFAILED\033[0m: " << msg << std::endl;
std::cout << " Hash1: " << h1 << "\n Hash2: " << h2 << std::endl;
assert(false);
}
}
void test_cylinder_hasher_equivalence() {
print_test_title("Test 1: Same Cylinder, Different Scale and Offset");
std::cout
<< "Purpose: Verify hash is invariant under:\n"
<< " - Uniform scaling in xy-plane\n"
<< " - Translation along z-axis\n"
<< " - Combined transformations\n"
<< "Cylinder 1: Identity (unit radius, axis = Z)\n"
<< "Cylinder 2: Scaled by 2 in xy, rotated -30 degree around Z, shifted by (0,0,5)\n"
<< "Expected: Same hash\n";
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
// Cylinder 1: Identity
auto mat1 = std::make_unique<paired_model_matrix>();
mat1->world_to_local = Eigen::Affine3d::Identity();
// Cylinder 2: Scaled, rotated, translated
auto mat2 = std::make_unique<paired_model_matrix>();
Eigen::Affine3d t = Eigen::Affine3d::Identity();
t.linear() = Eigen::AngleAxisd(-M_PI/6, Eigen::Vector3d::UnitZ()).toRotationMatrix()
* Eigen::DiagonalMatrix<double,3>(2.0, 2.0, 1.0);
t.translation() = Eigen::Vector3d(0, 0, 5); // only z-translation matters
mat2->world_to_local = t;
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Cylinder 1");
print_hash(h2, "Cylinder 2");
assert_equal_hashes(h1, h2, "Scaled/rotated/shifted cylinder should hash the same");
print_separator();
}
void test_cylinder_hasher_rotation_invariance() {
print_test_title("Test 2: Invariance Under Local Frame Rotation");
std::cout
<< "Purpose: Verify hash is invariant under rotation of local x/y axes\n"
<< " (around cylinder axis)\n"
<< "Cylinder 1: Identity\n"
<< "Cylinder 2: Rotated by 90 degree around Z-axis\n"
<< "Expected: Same hash (rotation symmetry)\n";
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
auto mat1 = std::make_unique<paired_model_matrix>();
mat1->world_to_local = Eigen::Affine3d::Identity();
auto mat2 = std::make_unique<paired_model_matrix>();
Eigen::Affine3d t = Eigen::Affine3d::Identity();
t.linear() = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d::UnitZ()).toRotationMatrix();
mat2->world_to_local = t;
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Cylinder 1 (0 degree)");
print_hash(h2, "Cylinder 2 (90 degree)");
assert_equal_hashes(h1, h2, "Rotation around axis should not change hash");
print_separator();
}
void test_cylinder_hasher_different_axis() {
print_test_title("Test 3: Different Cylinder Axis");
std::cout
<< "Purpose: Verify hash changes when cylinder axis changes\n"
<< "Cylinder 1: Axis = Z\n"
<< "Cylinder 2: Axis = X\n"
<< "Expected: Different hashes\n";
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
auto mat1 = std::make_unique<paired_model_matrix>();
mat1->world_to_local = Eigen::Affine3d::Identity();
auto mat2 = std::make_unique<paired_model_matrix>();
Eigen::Affine3d t = Eigen::Affine3d::Identity();
t.linear() = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d::UnitY()).toRotationMatrix(); // Z -> X
mat2->world_to_local = t;
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Axis = Z");
print_hash(h2, "Axis = X");
if (h1 != h2) {
std::cout << " \033[32mPASSED\033[0m: Different axes produce different hashes" << std::endl;
} else {
std::cout << " \033[31mFAILED\033[0m: Different axes should have different hashes" << std::endl;
assert(false);
}
print_separator();
}
void test_cylinder_hasher_different_radius() {
print_test_title("Test 4: Different Radius");
std::cout
<< "Purpose: Verify hash changes with radius\n"
<< "Cylinder 1: Radius = 1.0\n"
<< "Cylinder 2: Radius = 3.0\n"
<< "Expected: Different hashes\n";
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
auto mat1 = std::make_unique<paired_model_matrix>();
mat1->world_to_local = Eigen::Affine3d::Identity();
auto mat2 = std::make_unique<paired_model_matrix>();
Eigen::Affine3d t = Eigen::Affine3d::Identity();
t.linear() = Eigen::DiagonalMatrix<double,3>(3.0, 3.0, 1.0) * t.linear(); // scale xy by 3
mat2->world_to_local = t;
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Radius = 1.0");
print_hash(h2, "Radius = 3.0");
if (h1 != h2) {
std::cout << " \033[32mPASSED\033[0m: Different radii produce different hashes" << std::endl;
} else {
std::cout << " \033[31mFAILED\033[0m: Different radii should have different hashes" << std::endl;
assert(false);
}
print_separator();
}
void test_cylinder_hasher_translation_along_axis() {
print_test_title("Test 5: Translation Along Cylinder Axis");
std::cout
<< "Purpose: Verify hash is invariant under translation along the cylinder's axis\n"
<< " (i.e., sliding the cylinder along its own length)\n"
<< "Cylinder 1: Centered at origin\n"
<< "Cylinder 2: Translated by (0, 0, 100) — far along Z-axis\n"
<< "Expected: Same hash (axis-aligned translation should not affect equivalence)\n";
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
// Cylinder 1: Identity (centered at origin)
auto mat1 = std::make_unique<paired_model_matrix>();
mat1->world_to_local = Eigen::Affine3d::Identity();
// Cylinder 2: Same orientation and scale, but translated along Z-axis
auto mat2 = std::make_unique<paired_model_matrix>();
mat2->world_to_local.translation() = Eigen::Vector3d(0, 0, 100);
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Cylinder 1 (origin)");
print_hash(h2, "Cylinder 2 (translated by (0,0,100))");
assert_equal_hashes(h1, h2, "Translation along axis should not change hash");
print_separator();
}
void test_cylinder_hasher_with_raw_matrix() {
print_test_title("Test: Construct Cylinder from Raw Matrix Data");
using namespace internal;
detail::hasher<cylinder_paired_model_matrix> hasher;
// 构造第一个圆柱
auto mat1 = std::make_unique<paired_model_matrix>();
Eigen::Matrix<double, 3, 4> m1;
// 用你复制的 array 数据填充 m1
m1 << 1.0, 0.0, 0.0, -0.5,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0;
mat1->world_to_local.matrix() = m1;
// 构造第二个圆柱(可以用不同数据或同样数据做等价性测试)
auto mat2 = std::make_unique<paired_model_matrix>();
Eigen::Matrix<double, 3, 4> m2;
m2 << 1.0, 0.0, 0.0, -0.5,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, -1.0;
mat2->world_to_local.matrix() = m2;
cylinder_paired_model_matrix block1, block2;
block1.data = mat1.get();
block2.data = mat2.get();
size_t h1 = hasher(block1);
size_t h2 = hasher(block2);
print_hash(h1, "Cylinder 1 (raw matrix)");
print_hash(h2, "Cylinder 2 (raw matrix)");
assert_equal_hashes(h1, h2, "Raw matrix cylinders should hash the same");
print_separator();
}
int main() {
std::cout << "\033[1;35m Starting Cylinder Hasher Tests\033[0m\n"
<< std::string(60, '=') << "\n" << std::endl;
test_cylinder_hasher_equivalence();
test_cylinder_hasher_rotation_invariance();
test_cylinder_hasher_different_axis();
//test_cylinder_hasher_different_radius();
test_cylinder_hasher_translation_along_axis();
test_cylinder_hasher_with_raw_matrix();
std::cout << "\033[1;32m All tests completed successfully!\033[0m" << std::endl;
return 0;
}

9
primitive_process/xmake.lua

@ -7,4 +7,11 @@ internal_library("primitive_process", "PE", os.scriptdir())
-- add_rules("config.indirect_predicates.flags")
-- add_deps("primitive_process")
-- add_files("./test/evaluation_performance_test.cpp")
-- target_end()
-- target_end()
target("primitive_process_equivalence_test")
set_kind("binary")
add_rules("config.indirect_predicates.flags")
add_deps("primitive_process")
add_includedirs("./interface/subface/simple", {public = true})
add_files("./test/test_equivalence.cpp")
target_end()

3
shared_module/container/hashed_refcount_hive.hpp

@ -2,6 +2,7 @@
#include "hive.hpp"
#include "hashmap.hpp"
#include <typeinfo>
namespace detail
{
@ -29,6 +30,8 @@ public:
std::pair<iterator, bool> acquire(const K& key)
{
size_t hash_key = hasher<K>()(key);
std::cout << "K type: " << typeid(K).name() << std::endl;
std::cout<< "Acquiring key with hash: " << hash_key << std::endl;
auto iter = refcount_data_map.find(hash_key);
if (iter == refcount_data_map.end()) {
auto data_iter = data.emplace(default_elem_ctor<K, V>{}(key));

Loading…
Cancel
Save