FAILURE MAP
← Case archive

FA-87836 / Collision detection broadphase / Open access

Rotated proxy halves the already-half extents · case 01

Proxies are half the size of the shape and contacts are missed.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

ex is divided by two.

VERIFIED REPAIR

Inputs are half extents; use them directly.

Unsuccessful approach: Doubling the extent treats them as full sizes the other way.

Case contract

solve(cx, cy, hx, hy, angle, margin): broadphase proxy for a box with centre, half extents and rotation in degrees. Half sizes ex=|cos|hx+|sin|hy, ey=|sin|hx+|cos|hy; fatten by margin on every side. Return [minx, miny, maxx, maxy] rounded to 6.

Why this case matters

Broadphase stages decide which object pairs ever reach narrowphase; a wrong boundary, ordering or bookkeeping rule silently drops real contacts or floods the solver with false candidates.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(cx, cy, hx, hy, angle, margin):
    c, s = abs(math.cos(math.radians(angle))), abs(math.sin(math.radians(angle)))
    ex = (c * hx + s * hy) / 2
    ey = s * hx + c * hy
    return [round(cx - ex - margin, 6), round(cy - ey - margin, 6), round(cx + ex + margin, 6), round(cy + ey + margin, 6)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[4.14, -0.89, 0.57, 1.72, 200, 0.25], [2.766101, -2.951223, 5.513899, 1.171223]], [[1.85, -4.76, 1.97, 1.2, 45, 0.25], [-0.641528, -7.251528, 4.341528, -2.268472]], [[-1.12, -3.37, 2.49, 0.42, 200, 0.1], [-3.703483, -4.716301, 1.463483, -2.023699]], [[4.44, 0.93, 2.05, 2.81, 200, 0.1], [1.452554, -2.511678, 7.427446, 4.371678]], [[3.5, -2.67, 1.45, 1.16, 45, 0.25], [1.404451, -4.765549, 5.595549, -0.574451]], [[1.77, -3.53, 2.26, 2.35, 30, 0.1], [-1.462217, -6.79516, 5.002217, -0.26484]], [[0.72, 3.51, 1.08, 2.11, 120, 0.1], [-1.747314, 1.419693, 3.187314, 5.600307]], [[0.02, -4.86, 1.53, 2.94, 0, 0.25], [-1.76, -8.05, 1.8, -1.67]]], [[[-1.23, -2.52, 1.88, 1.06, 0, 0.1], [-3.21, -3.68, 0.75, -1.36]], [[-0.1, 0.09, 1.24, 0.5, 30, 0.1], [-1.523872, -1.063013, 1.323872, 1.243013]], [[-0.58, -1.09, 0.34, 2.54, 90, 0.1], [-3.22, -1.53, 2.06, -0.65]], [[2.96, 2.58, 1.99, 1.59, 315, 0.1], [0.328558, -0.051442, 5.591442, 5.211442]], [[4.97, -3.01, 0.79, 0.48, 315, 0.0], [4.071974, -3.908026, 5.868026, -2.111974]], [[-3.14, 0.32, 1.05, 2.01, 315, 0.1], [-5.403747, -1.943747, -0.876253, 2.583747]], [[-0.44, -0.64, 2.18, 1.13, 45, 0.25], [-3.030523, -3.230523, 2.150523, 1.950523]], [[3.82, 1.95, 1.76, 0.82, 315, 0.0], [1.995665, 0.125665, 5.644335, 3.774335]]], [[[-4.88, -0.85, 2.26, 1.51, 90, 0.25], [-6.64, -3.36, -3.12, 1.66]], [[2.15, -0.65, 0.42, 1.49, 315, 0.25], [0.549426, -2.250574, 3.750574, 0.950574]], [[-3.34, -2.28, 2.85, 2.54, 120, 0.1], [-7.064705, -6.118172, 0.384705, 1.558172]], [[4.54, -1.65, 1.92, 0.9, 45, 0.1], [2.445959, -3.744041, 6.634041, 0.444041]], [[2.62, -2.5, 0.78, 1.78, 30, 0.25], [0.8045, -4.681525, 4.4355, -0.318475]], [[2.84, -2.54, 0.99, 1.26, 0, 0.0], [1.85, -3.8, 3.83, -1.28]], [[0.68, -2.75, 2.84, 1.6, 200, 0.1], [-2.635959, -5.324845, 3.995959, -0.175155]], [[3.29, -2.89, 0.26, 2.98, 315, 0.25], [0.748974, -5.431026, 5.831026, -0.348974]]], [[[1.03, 3.28, 1.97, 1.8, 90, 0.1], [-0.87, 1.21, 2.93, 5.35]], [[2.89, -1.08, 1.78, 2.99, 45, 0.25], [-0.732899, -4.702899, 6.512899, 2.542899]], [[1.06, 3.12, 2.75, 0.46, 120, 0.0], [-0.713372, 0.50843, 2.833372, 5.73157]], [[-1.04, -1.98, 0.76, 0.88, 120, 0.25], [-2.432102, -3.328179, 0.352102, -0.631821]], [[2.09, 2.94, 1.51, 1.33, -60, 0.25], [-0.066814, 0.717302, 4.246814, 5.162698]], [[-4.22, -2.52, 1.36, 2.06, 45, 0.25], [-6.888305, -5.188305, -1.551695, 0.148305]], [[-1.24, -3.47, 2.98, 2.05, 0, 0.25], [-4.47, -5.77, 1.99, -1.17]], [[-4.35, 0.43, 1.25, 1.94, 90, 0.25], [-6.54, -1.07, -2.16, 1.93]]], [[[-3.4, -4.45, 1.47, 0.71, 45, 0.1], [-5.041493, -6.091493, -1.758507, -2.808507]], [[-2.63, 3.94, 1.14, 1.37, 45, 0.25], [-4.654838, 1.915162, -0.605162, 5.964838]], [[-1.12, -3.66, 1.94, 1.1, 315, 0.1], [-3.369605, -5.909605, 1.129605, -1.410395]], [[-1.98, 0.45, 1.99, 0.94, 90, 0.1], [-3.02, -1.64, -0.94, 2.54]], [[4.11, 0.63, 2.66, 0.49, -60, 0.25], [2.105648, -2.168628, 6.114352, 3.428628]], [[-1.73, -1.68, 0.29, 1.98, 30, 0.25], [-3.221147, -3.78973, -0.238853, 0.42973]], [[2.04, 2.02, 0.4, 1.03, 315, 0.1], [0.928837, 0.908837, 3.151163, 3.131163]], [[-1.51, -4.04, 1.87, 1.15, -60, 0.1], [-3.540929, -6.334468, 0.520929, -1.745532]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[3.32805, -2.951223, 4.95195, 1.171223][2.766101, -2.951223, 5.513899, 1.171223]Failed
case 1[0.479236, -7.251528, 3.220764, -2.268472][-0.641528, -7.251528, 4.341528, -2.268472]Failed
case 2[-2.461742, -4.716301, 0.221742, -2.023699][-3.703483, -4.716301, 1.463483, -2.023699]Failed
case 3[2.896277, -2.511678, 5.983723, 4.371678][1.452554, -2.511678, 7.427446, 4.371678]Failed
case 4[2.327226, -4.765549, 4.672774, -0.574451][1.404451, -4.765549, 5.595549, -0.574451]Failed
case 5[0.103891, -6.79516, 3.436109, -0.26484][-1.462217, -6.79516, 5.002217, -0.26484]Failed
case 6[-0.563657, 1.419693, 2.003657, 5.600307][-1.747314, 1.419693, 3.187314, 5.600307]Failed
case 7[-0.995, -8.05, 1.035, -1.67][-1.76, -8.05, 1.8, -1.67]Failed

SHA-256 / 9cc9ed7497fc60fff72d06158ce00ca6cfaef46b0ac3e991b53b3d4be8289bc8

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(cx, cy, hx, hy, angle, margin):
    c, s = abs(math.cos(math.radians(angle))), abs(math.sin(math.radians(angle)))
    ex = 2 * (c * hx + s * hy)
    ey = s * hx + c * hy
    return [round(cx - ex - margin, 6), round(cy - ey - margin, 6), round(cx + ex + margin, 6), round(cy + ey + margin, 6)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[4.14, -0.89, 0.57, 1.72, 200, 0.25], [2.766101, -2.951223, 5.513899, 1.171223]], [[1.85, -4.76, 1.97, 1.2, 45, 0.25], [-0.641528, -7.251528, 4.341528, -2.268472]], [[-1.12, -3.37, 2.49, 0.42, 200, 0.1], [-3.703483, -4.716301, 1.463483, -2.023699]], [[4.44, 0.93, 2.05, 2.81, 200, 0.1], [1.452554, -2.511678, 7.427446, 4.371678]], [[3.5, -2.67, 1.45, 1.16, 45, 0.25], [1.404451, -4.765549, 5.595549, -0.574451]], [[1.77, -3.53, 2.26, 2.35, 30, 0.1], [-1.462217, -6.79516, 5.002217, -0.26484]], [[0.72, 3.51, 1.08, 2.11, 120, 0.1], [-1.747314, 1.419693, 3.187314, 5.600307]], [[0.02, -4.86, 1.53, 2.94, 0, 0.25], [-1.76, -8.05, 1.8, -1.67]]], [[[-1.23, -2.52, 1.88, 1.06, 0, 0.1], [-3.21, -3.68, 0.75, -1.36]], [[-0.1, 0.09, 1.24, 0.5, 30, 0.1], [-1.523872, -1.063013, 1.323872, 1.243013]], [[-0.58, -1.09, 0.34, 2.54, 90, 0.1], [-3.22, -1.53, 2.06, -0.65]], [[2.96, 2.58, 1.99, 1.59, 315, 0.1], [0.328558, -0.051442, 5.591442, 5.211442]], [[4.97, -3.01, 0.79, 0.48, 315, 0.0], [4.071974, -3.908026, 5.868026, -2.111974]], [[-3.14, 0.32, 1.05, 2.01, 315, 0.1], [-5.403747, -1.943747, -0.876253, 2.583747]], [[-0.44, -0.64, 2.18, 1.13, 45, 0.25], [-3.030523, -3.230523, 2.150523, 1.950523]], [[3.82, 1.95, 1.76, 0.82, 315, 0.0], [1.995665, 0.125665, 5.644335, 3.774335]]], [[[-4.88, -0.85, 2.26, 1.51, 90, 0.25], [-6.64, -3.36, -3.12, 1.66]], [[2.15, -0.65, 0.42, 1.49, 315, 0.25], [0.549426, -2.250574, 3.750574, 0.950574]], [[-3.34, -2.28, 2.85, 2.54, 120, 0.1], [-7.064705, -6.118172, 0.384705, 1.558172]], [[4.54, -1.65, 1.92, 0.9, 45, 0.1], [2.445959, -3.744041, 6.634041, 0.444041]], [[2.62, -2.5, 0.78, 1.78, 30, 0.25], [0.8045, -4.681525, 4.4355, -0.318475]], [[2.84, -2.54, 0.99, 1.26, 0, 0.0], [1.85, -3.8, 3.83, -1.28]], [[0.68, -2.75, 2.84, 1.6, 200, 0.1], [-2.635959, -5.324845, 3.995959, -0.175155]], [[3.29, -2.89, 0.26, 2.98, 315, 0.25], [0.748974, -5.431026, 5.831026, -0.348974]]], [[[1.03, 3.28, 1.97, 1.8, 90, 0.1], [-0.87, 1.21, 2.93, 5.35]], [[2.89, -1.08, 1.78, 2.99, 45, 0.25], [-0.732899, -4.702899, 6.512899, 2.542899]], [[1.06, 3.12, 2.75, 0.46, 120, 0.0], [-0.713372, 0.50843, 2.833372, 5.73157]], [[-1.04, -1.98, 0.76, 0.88, 120, 0.25], [-2.432102, -3.328179, 0.352102, -0.631821]], [[2.09, 2.94, 1.51, 1.33, -60, 0.25], [-0.066814, 0.717302, 4.246814, 5.162698]], [[-4.22, -2.52, 1.36, 2.06, 45, 0.25], [-6.888305, -5.188305, -1.551695, 0.148305]], [[-1.24, -3.47, 2.98, 2.05, 0, 0.25], [-4.47, -5.77, 1.99, -1.17]], [[-4.35, 0.43, 1.25, 1.94, 90, 0.25], [-6.54, -1.07, -2.16, 1.93]]], [[[-3.4, -4.45, 1.47, 0.71, 45, 0.1], [-5.041493, -6.091493, -1.758507, -2.808507]], [[-2.63, 3.94, 1.14, 1.37, 45, 0.25], [-4.654838, 1.915162, -0.605162, 5.964838]], [[-1.12, -3.66, 1.94, 1.1, 315, 0.1], [-3.369605, -5.909605, 1.129605, -1.410395]], [[-1.98, 0.45, 1.99, 0.94, 90, 0.1], [-3.02, -1.64, -0.94, 2.54]], [[4.11, 0.63, 2.66, 0.49, -60, 0.25], [2.105648, -2.168628, 6.114352, 3.428628]], [[-1.73, -1.68, 0.29, 1.98, 30, 0.25], [-3.221147, -3.78973, -0.238853, 0.42973]], [[2.04, 2.02, 0.4, 1.03, 315, 0.1], [0.928837, 0.908837, 3.151163, 3.131163]], [[-1.51, -4.04, 1.87, 1.15, -60, 0.1], [-3.540929, -6.334468, 0.520929, -1.745532]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[1.642201, -2.951223, 6.637799, 1.171223][2.766101, -2.951223, 5.513899, 1.171223]Failed
case 1[-2.883057, -7.251528, 6.583057, -2.268472][-0.641528, -7.251528, 4.341528, -2.268472]Failed
case 2[-6.186966, -4.716301, 3.946966, -2.023699][-3.703483, -4.716301, 1.463483, -2.023699]Failed
case 3[-1.434893, -2.511678, 10.314893, 4.371678][1.452554, -2.511678, 7.427446, 4.371678]Failed
case 4[-0.441097, -4.765549, 7.441097, -0.574451][1.404451, -4.765549, 5.595549, -0.574451]Failed
case 5[-4.594435, -6.79516, 8.134435, -0.26484][-1.462217, -6.79516, 5.002217, -0.26484]Failed
case 6[-4.114627, 1.419693, 5.554627, 5.600307][-1.747314, 1.419693, 3.187314, 5.600307]Failed
case 7[-3.29, -8.05, 3.33, -1.67][-1.76, -8.05, 1.8, -1.67]Failed

SHA-256 / c5f4676007e82bbfffba06ba6b846d774354f9882e519d66bc72cf83e2867c27

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(cx, cy, hx, hy, angle, margin):
    c, s = abs(math.cos(math.radians(angle))), abs(math.sin(math.radians(angle)))
    ex = c * hx + s * hy
    ey = s * hx + c * hy
    return [round(cx - ex - margin, 6), round(cy - ey - margin, 6), round(cx + ex + margin, 6), round(cy + ey + margin, 6)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[4.14, -0.89, 0.57, 1.72, 200, 0.25], [2.766101, -2.951223, 5.513899, 1.171223]], [[1.85, -4.76, 1.97, 1.2, 45, 0.25], [-0.641528, -7.251528, 4.341528, -2.268472]], [[-1.12, -3.37, 2.49, 0.42, 200, 0.1], [-3.703483, -4.716301, 1.463483, -2.023699]], [[4.44, 0.93, 2.05, 2.81, 200, 0.1], [1.452554, -2.511678, 7.427446, 4.371678]], [[3.5, -2.67, 1.45, 1.16, 45, 0.25], [1.404451, -4.765549, 5.595549, -0.574451]], [[1.77, -3.53, 2.26, 2.35, 30, 0.1], [-1.462217, -6.79516, 5.002217, -0.26484]], [[0.72, 3.51, 1.08, 2.11, 120, 0.1], [-1.747314, 1.419693, 3.187314, 5.600307]], [[0.02, -4.86, 1.53, 2.94, 0, 0.25], [-1.76, -8.05, 1.8, -1.67]]], [[[-1.23, -2.52, 1.88, 1.06, 0, 0.1], [-3.21, -3.68, 0.75, -1.36]], [[-0.1, 0.09, 1.24, 0.5, 30, 0.1], [-1.523872, -1.063013, 1.323872, 1.243013]], [[-0.58, -1.09, 0.34, 2.54, 90, 0.1], [-3.22, -1.53, 2.06, -0.65]], [[2.96, 2.58, 1.99, 1.59, 315, 0.1], [0.328558, -0.051442, 5.591442, 5.211442]], [[4.97, -3.01, 0.79, 0.48, 315, 0.0], [4.071974, -3.908026, 5.868026, -2.111974]], [[-3.14, 0.32, 1.05, 2.01, 315, 0.1], [-5.403747, -1.943747, -0.876253, 2.583747]], [[-0.44, -0.64, 2.18, 1.13, 45, 0.25], [-3.030523, -3.230523, 2.150523, 1.950523]], [[3.82, 1.95, 1.76, 0.82, 315, 0.0], [1.995665, 0.125665, 5.644335, 3.774335]]], [[[-4.88, -0.85, 2.26, 1.51, 90, 0.25], [-6.64, -3.36, -3.12, 1.66]], [[2.15, -0.65, 0.42, 1.49, 315, 0.25], [0.549426, -2.250574, 3.750574, 0.950574]], [[-3.34, -2.28, 2.85, 2.54, 120, 0.1], [-7.064705, -6.118172, 0.384705, 1.558172]], [[4.54, -1.65, 1.92, 0.9, 45, 0.1], [2.445959, -3.744041, 6.634041, 0.444041]], [[2.62, -2.5, 0.78, 1.78, 30, 0.25], [0.8045, -4.681525, 4.4355, -0.318475]], [[2.84, -2.54, 0.99, 1.26, 0, 0.0], [1.85, -3.8, 3.83, -1.28]], [[0.68, -2.75, 2.84, 1.6, 200, 0.1], [-2.635959, -5.324845, 3.995959, -0.175155]], [[3.29, -2.89, 0.26, 2.98, 315, 0.25], [0.748974, -5.431026, 5.831026, -0.348974]]], [[[1.03, 3.28, 1.97, 1.8, 90, 0.1], [-0.87, 1.21, 2.93, 5.35]], [[2.89, -1.08, 1.78, 2.99, 45, 0.25], [-0.732899, -4.702899, 6.512899, 2.542899]], [[1.06, 3.12, 2.75, 0.46, 120, 0.0], [-0.713372, 0.50843, 2.833372, 5.73157]], [[-1.04, -1.98, 0.76, 0.88, 120, 0.25], [-2.432102, -3.328179, 0.352102, -0.631821]], [[2.09, 2.94, 1.51, 1.33, -60, 0.25], [-0.066814, 0.717302, 4.246814, 5.162698]], [[-4.22, -2.52, 1.36, 2.06, 45, 0.25], [-6.888305, -5.188305, -1.551695, 0.148305]], [[-1.24, -3.47, 2.98, 2.05, 0, 0.25], [-4.47, -5.77, 1.99, -1.17]], [[-4.35, 0.43, 1.25, 1.94, 90, 0.25], [-6.54, -1.07, -2.16, 1.93]]], [[[-3.4, -4.45, 1.47, 0.71, 45, 0.1], [-5.041493, -6.091493, -1.758507, -2.808507]], [[-2.63, 3.94, 1.14, 1.37, 45, 0.25], [-4.654838, 1.915162, -0.605162, 5.964838]], [[-1.12, -3.66, 1.94, 1.1, 315, 0.1], [-3.369605, -5.909605, 1.129605, -1.410395]], [[-1.98, 0.45, 1.99, 0.94, 90, 0.1], [-3.02, -1.64, -0.94, 2.54]], [[4.11, 0.63, 2.66, 0.49, -60, 0.25], [2.105648, -2.168628, 6.114352, 3.428628]], [[-1.73, -1.68, 0.29, 1.98, 30, 0.25], [-3.221147, -3.78973, -0.238853, 0.42973]], [[2.04, 2.02, 0.4, 1.03, 315, 0.1], [0.928837, 0.908837, 3.151163, 3.131163]], [[-1.51, -4.04, 1.87, 1.15, -60, 0.1], [-3.540929, -6.334468, 0.520929, -1.745532]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[2.766101, -2.951223, 5.513899, 1.171223][2.766101, -2.951223, 5.513899, 1.171223]Passed
case 1[-0.641528, -7.251528, 4.341528, -2.268472][-0.641528, -7.251528, 4.341528, -2.268472]Passed
case 2[-3.703483, -4.716301, 1.463483, -2.023699][-3.703483, -4.716301, 1.463483, -2.023699]Passed
case 3[1.452554, -2.511678, 7.427446, 4.371678][1.452554, -2.511678, 7.427446, 4.371678]Passed
case 4[1.404451, -4.765549, 5.595549, -0.574451][1.404451, -4.765549, 5.595549, -0.574451]Passed
case 5[-1.462217, -6.79516, 5.002217, -0.26484][-1.462217, -6.79516, 5.002217, -0.26484]Passed
case 6[-1.747314, 1.419693, 3.187314, 5.600307][-1.747314, 1.419693, 3.187314, 5.600307]Passed
case 7[-1.76, -8.05, 1.8, -1.67][-1.76, -8.05, 1.8, -1.67]Passed

SHA-256 / fcb77eae82007b8e7f143df767333ac3a659ab39652f70411e19779199a49ff4

Verification & scope

A deterministic bounded teaching model with stipulated toy conventions; not a production physics engine or spatial index. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:51:02.579956+00:00.

Case digest / 7b773537ce9862e9cf9fe726912c066bbca69d4491c323b607b467a0ef2b002f