|
| 1 | +from re import I |
1 | 2 | import pytest |
2 | 3 | import numpy as np |
3 | 4 | # scipy sparse matrices |
|
12 | 13 | import igl.copyleft.cgal |
13 | 14 | import igl.embree |
14 | 15 |
|
| 16 | +@pytest.fixture |
| 17 | +def icosahedron(): |
| 18 | + V,F = igl.icosahedron() |
| 19 | + return V,F |
15 | 20 |
|
16 | 21 | #def rand_sparse(n,density): |
17 | 22 | # n_features = n |
@@ -600,3 +605,138 @@ def udf_sphere(Q): |
600 | 605 | unique_ijk, J, unique_corners = igl.unique_sparse_voxel_corners(origin,h0,max_depth,ijk) |
601 | 606 | unique_S = sdf_sphere(unique_corners) |
602 | 607 | V,F = igl.marching_cubes(unique_S,unique_corners,J,0.0) |
| 608 | + |
| 609 | +def test_rotate_vectors(icosahedron): |
| 610 | + V,F = icosahedron |
| 611 | + |
| 612 | + # Create rotation angles (rotate by pi/4) |
| 613 | + A = np.ones(F.shape[0],dtype=np.float64) * (np.pi / 4.0) |
| 614 | + |
| 615 | + # Get local basis |
| 616 | + B1,B2,_ = igl.local_basis(V,F) |
| 617 | + |
| 618 | + # B1 is orthogonal to B2 |
| 619 | + r = np.sum(B1* B2, axis=1) |
| 620 | + assert np.allclose(np.abs(r), 0.0) |
| 621 | + |
| 622 | + # Rotate the first basis vector |
| 623 | + B1_rotated = igl.rotate_vectors(B1, A, B1, B2) |
| 624 | + |
| 625 | + # Check output shape |
| 626 | + assert B1_rotated.shape == B1.shape |
| 627 | + |
| 628 | + # Rotate B1_rotated by pi/4 again |
| 629 | + B1_rotated2 = igl.rotate_vectors(B1_rotated, A, B1, B2) |
| 630 | + assert B1_rotated2.shape == B1_rotated.shape |
| 631 | + |
| 632 | + # B1_rotated2 should be parallel to B2 |
| 633 | + r = np.sum(B1_rotated2 * B2, axis=1) |
| 634 | + assert np.allclose(np.abs(r), 1.0) |
| 635 | + |
| 636 | +def test_compute_frame_field_bisectors(icosahedron): |
| 637 | + V,F = icosahedron |
| 638 | + |
| 639 | + # Get local basis |
| 640 | + B1,B2,_ = igl.local_basis(V,F) |
| 641 | + |
| 642 | + # Compute bisectors with explicit basis |
| 643 | + BIS1,BIS2 = igl.compute_frame_field_bisectors(V,F,B1,B2,B1,B2) |
| 644 | + |
| 645 | + # Check output shapes |
| 646 | + assert BIS1.shape == (F.shape[0], 3) |
| 647 | + assert BIS2.shape == (F.shape[0], 3) |
| 648 | + |
| 649 | + # BIS1 should be orthogonal to BIS2 |
| 650 | + r = np.sum(BIS1 * BIS2, axis=1) |
| 651 | + assert np.allclose(np.abs(r), 0.0) |
| 652 | + |
| 653 | +def test_comb_cross_field(icosahedron): |
| 654 | + V,F = icosahedron |
| 655 | + |
| 656 | + # Get principal curvature directions (cross field) |
| 657 | + PD1,PD2,_,_,_ = igl.principal_curvature(V,F) |
| 658 | + |
| 659 | + # Comb the cross field |
| 660 | + PD1_combed,PD2_combed = igl.comb_cross_field(V,F,PD1,PD2) |
| 661 | + |
| 662 | + # Check output shapes |
| 663 | + assert PD1_combed.shape == (F.shape[0], 3) |
| 664 | + assert PD2_combed.shape == (F.shape[0], 3) |
| 665 | + |
| 666 | +def test_cross_field_mismatch(icosahedron): |
| 667 | + V,F = icosahedron |
| 668 | + |
| 669 | + # Get principal curvature directions (cross field) |
| 670 | + PD1,PD2,_,_,_ = igl.principal_curvature(V,F) |
| 671 | + |
| 672 | + # Comb the cross field first |
| 673 | + PD1_combed,PD2_combed = igl.comb_cross_field(V,F,PD1,PD2) |
| 674 | + |
| 675 | + # Compute mismatch on combed field |
| 676 | + mismatch = igl.cross_field_mismatch(V,F,PD1_combed,PD2_combed,True) |
| 677 | + |
| 678 | + # Check output shape (should be #F by 3 for triangular mesh) |
| 679 | + assert mismatch.shape == (F.shape[0], 3) |
| 680 | + |
| 681 | + # Test with uncombed field (function will comb it first) |
| 682 | + mismatch_uncombed = igl.cross_field_mismatch(V,F,PD1,PD2,False) |
| 683 | + assert mismatch_uncombed.shape == (F.shape[0], 3) |
| 684 | + |
| 685 | +def test_find_cross_field_singularities(icosahedron): |
| 686 | + V,F = icosahedron |
| 687 | + |
| 688 | + # Get principal curvature directions (cross field) |
| 689 | + PD1,PD2,_,_,_ = igl.principal_curvature(V,F) |
| 690 | + |
| 691 | + # Comb the cross field |
| 692 | + PD1_combed,PD2_combed = igl.comb_cross_field(V,F,PD1,PD2) |
| 693 | + |
| 694 | + # Compute mismatch |
| 695 | + mismatch = igl.cross_field_mismatch(V,F,PD1_combed,PD2_combed,True) |
| 696 | + |
| 697 | + # Find singularities from mismatch |
| 698 | + isSingularity,singularityIndex = igl.find_cross_field_singularities(V,F,mismatch) |
| 699 | + |
| 700 | + # Check Poincaré-Hopf theorem |
| 701 | + assert np.sum(singularityIndex) == 2 * 4 # Euler characteristic * 4-rosy fields |
| 702 | + |
| 703 | + # Check output shapes |
| 704 | + assert isSingularity.shape[0] == V.shape[0] |
| 705 | + assert singularityIndex.shape[0] == V.shape[0] |
| 706 | + |
| 707 | + # Test overload that computes mismatch internally |
| 708 | + isSingularity2,singularityIndex2 = igl.find_cross_field_singularities(V,F,PD1_combed,PD2_combed,True) |
| 709 | + |
| 710 | + # Check Poincaré-Hopf theorem |
| 711 | + assert np.sum(singularityIndex2) == 2 * 4 # Euler characteristic * 4-rosy fields |
| 712 | + |
| 713 | + # Check output shapes |
| 714 | + assert isSingularity2.shape[0] == V.shape[0] |
| 715 | + assert singularityIndex2.shape[0] == V.shape[0] |
| 716 | + |
| 717 | + # Test with uncombed field |
| 718 | + isSingularity3,singularityIndex3 = igl.find_cross_field_singularities(V,F,PD1,PD2,False) |
| 719 | + assert isSingularity3.shape[0] == V.shape[0] |
| 720 | + assert singularityIndex3.shape[0] == V.shape[0] |
| 721 | + |
| 722 | + # Check Poincaré-Hopf theorem |
| 723 | + assert np.sum(singularityIndex3) == 2 * 4 # Euler characteristic * 4-rosy fields |
| 724 | + |
| 725 | +def test_comb_frame_field(icosahedron): |
| 726 | + V,F = icosahedron |
| 727 | + |
| 728 | + # Get principal curvature directions (frame field) |
| 729 | + PD1,PD2,_,_,_ = igl.principal_curvature(V,F) |
| 730 | + |
| 731 | + # Compute bisectors |
| 732 | + BIS1,BIS2 = igl.compute_frame_field_bisectors(V,F,PD1,PD2) |
| 733 | + |
| 734 | + # Comb the bisectors (which are a cross field) |
| 735 | + BIS1_combed,BIS2_combed = igl.comb_cross_field(V,F,BIS1,BIS2) |
| 736 | + |
| 737 | + # Comb the frame field using combed bisectors |
| 738 | + PD1_combed,PD2_combed = igl.comb_frame_field(V,F,PD1,PD2,BIS1_combed,BIS2_combed) |
| 739 | + |
| 740 | + # Check output shapes |
| 741 | + assert PD1_combed.shape == (F.shape[0], 3) |
| 742 | + assert PD2_combed.shape == (F.shape[0], 3) |
0 commit comments