@@ -120,6 +120,40 @@ def is_contact_already_exported(
120120 return False
121121
122122
123+ @wp .func
124+ def _floats_are_near_ulps (a : float , b : float ) -> bool :
125+ """Return whether two floats are close in ULPs and absolute magnitude."""
126+ if wp .abs (a - b ) > 1.0e-8 :
127+ return False
128+ a_bits = float_flip (a )
129+ b_bits = float_flip (b )
130+ if a_bits > b_bits :
131+ return a_bits - b_bits <= wp .uint32 (16 )
132+ return b_bits - a_bits <= wp .uint32 (16 )
133+
134+
135+ @wp .func
136+ def _contacts_are_numerically_equivalent (
137+ contact_a : int ,
138+ contact_b : int ,
139+ position_depth : wp .array [wp .vec4 ],
140+ normal : wp .array [wp .vec2 ],
141+ ) -> bool :
142+ """Return whether two buffered contacts differ only by float rounding."""
143+ pd_a = position_depth [contact_a ]
144+ pd_b = position_depth [contact_b ]
145+ n_a = normal [contact_a ]
146+ n_b = normal [contact_b ]
147+ return (
148+ _floats_are_near_ulps (pd_a [0 ], pd_b [0 ])
149+ and _floats_are_near_ulps (pd_a [1 ], pd_b [1 ])
150+ and _floats_are_near_ulps (pd_a [2 ], pd_b [2 ])
151+ and _floats_are_near_ulps (pd_a [3 ], pd_b [3 ])
152+ and _floats_are_near_ulps (n_a [0 ], n_b [0 ])
153+ and _floats_are_near_ulps (n_a [1 ], n_b [1 ])
154+ )
155+
156+
123157@wp .func
124158def compute_effective_radius (shape_type : int , shape_scale : wp .vec4 ) -> float :
125159 """Compute effective radius for a shape based on its type.
@@ -1619,7 +1653,7 @@ def export_reduced_contacts_kernel(
16191653 exported_ids = exported_ids_vec ()
16201654 num_exported = int (0 )
16211655
1622- # Read all value slots for this entry (slot-major layout)
1656+ # Read all value slots for this entry (slot-major layout).
16231657 for slot in range (wp .static (VALUES_PER_KEY )):
16241658 value = ht_values [slot * ht_capacity + entry_idx ]
16251659
@@ -1638,6 +1672,28 @@ def export_reduced_contacts_kernel(
16381672 exported_ids [num_exported ] = contact_id
16391673 num_exported = num_exported + 1
16401674
1675+ # Suppress roundoff-equivalent winners using the lower topology
1676+ # fingerprint. This examines at most 21 pairs per seven-slot entry.
1677+ duplicate_mask = int (0 )
1678+ for local_idx in range (num_exported ):
1679+ contact_id = exported_ids [local_idx ]
1680+ fingerprint = contact_fingerprints [contact_id ]
1681+ for other_idx in range (local_idx ):
1682+ other_id = exported_ids [other_idx ]
1683+ if _contacts_are_numerically_equivalent (contact_id , other_id , position_depth , normal ):
1684+ other_fingerprint = contact_fingerprints [other_id ]
1685+ if fingerprint < other_fingerprint :
1686+ duplicate_mask = duplicate_mask | (1 << other_idx )
1687+ else :
1688+ duplicate_mask = duplicate_mask | (1 << local_idx )
1689+
1690+ for local_idx in range (num_exported ):
1691+ if duplicate_mask & (1 << local_idx ) != 0 :
1692+ continue
1693+
1694+ contact_id = exported_ids [local_idx ]
1695+ fingerprint = contact_fingerprints [contact_id ]
1696+
16411697 # Cross-entry dedup: same contact can win slots in different entries
16421698 # (e.g., normal-bin AND voxel entry). Atomic flag per contact_id.
16431699 old_flag = wp .atomic_add (exported_flags , contact_id , 1 )
@@ -1677,7 +1733,7 @@ def export_reduced_contacts_kernel(
16771733 contact_data .shape_a = shape_a
16781734 contact_data .shape_b = shape_b
16791735 contact_data .gap_sum = gap_sum
1680- contact_data .sort_sub_key = contact_fingerprints [ contact_id ]
1736+ contact_data .sort_sub_key = fingerprint
16811737
16821738 # Call the writer function
16831739 writer_func (contact_data , writer_data , - 1 )
0 commit comments