{"record":{"id":"602a99a6677b4157","repo":"Genesis-Embodied-AI/genesis-world","slug":"cap-axis-must-be-non-zero","errorCode":null,"errorMessage":"cap_axis must be non-zero","messagePattern":"cap_axis must be non-zero","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"genesis/utils/geom.py","lineNumber":2364,"sourceCode":"\n    Returns\n    -------\n    points : np.ndarray, shape (N, 3)\n        Points on the sphere surface.\n    normals : np.ndarray, shape (N, 3), optional\n        Normal vectors of the points. Only returned if ``return_normals`` is True.\n    \"\"\"\n    if radius <= 0.0:\n        raise ValueError(f\"radius must be positive, got {radius}\")\n    if n_rings < 1:\n        raise ValueError(f\"n_rings must be >= 1, got {n_rings}\")\n    if arc_spacing <= 0.0:\n        raise ValueError(f\"probe_arc_spacing must be positive, got {arc_spacing}\")\n\n    pole = np.asarray(cap_axis, dtype=gs.np_float)\n    p_norm = float(np.linalg.norm(pole))\n    if p_norm < gs.EPS:\n        raise ValueError(\"cap_axis must be non-zero\")\n    pole = pole / p_norm\n    t0, t1 = orthogonals(pole)\n\n    pts: list[np.ndarray] = []\n    denom = max(n_rings - 1, 1)\n    for i in range(n_rings):\n        theta = (i / denom) * (0.5 * np.pi)\n        sin_t, cos_t = np.sin(theta), np.cos(theta)\n        ring_r = radius * sin_t\n        circ = 2.0 * np.pi * ring_r\n        if ring_r <= radius * gs.EPS:\n            n_pts = 1\n        else:\n            n_pts = max(3, int(np.ceil(circ / arc_spacing)))\n        for j in range(n_pts):\n            psi = (j / n_pts) * (2.0 * np.pi)\n            direction = sin_t * (np.cos(psi) * t0 + np.sin(psi) * t1) + cos_t * pole\n            pts.append(radius * direction)","sourceCodeStart":2346,"sourceCodeEnd":2382,"githubUrl":"https://github.com/Genesis-Embodied-AI/genesis-world/blob/56e4aa5d82bdb83f2e532e4cc364cb69527acb06/genesis/utils/geom.py#L2346-L2382","documentation":"The cap-sphere sampling helper normalizes cap_axis to get the pole direction of the spherical cap; a zero vector cannot be normalized into a direction, so it is rejected. The norm is compared against gs.EPS, meaning near-zero axes (denormalized or extremely small components) also fail. This mirrors the radius and arc_spacing checks in the same function: all cap geometry parameters must be well-formed before sampling.","triggerScenarios":"Calling the cap-sphere sampling helper with cap_axis=[0,0,0] or [0,0,1e-12], or with an axis computed as a cross product of two (near-)parallel vectors, which yields a zero norm.","commonSituations":"Deriving cap_axis from surface normals of degenerate geometry, or from cross(a, b) where a and b are parallel; also passing an uninitialized np.zeros(3) default.","solutions":["Pass an explicit unit axis such as cap_axis=[0, 0, 1].","If the axis comes from a cross product, verify the inputs are not parallel before calling.","If the axis is computed, fall back to a default direction when its norm falls below gs.EPS."],"exampleFix":"# before\naxis = np.cross(v1, v2)  # v1 parallel to v2 -> zero vector\npts = sample_cap_probe_points(radius=r, n_rings=5, arc_spacing=s, cap_axis=axis)\n# after\naxis = np.cross(v1, v2)\nif np.linalg.norm(axis) < gs.EPS:\n    axis = np.array([0.0, 0.0, 1.0])\npts = sample_cap_probe_points(radius=r, n_rings=5, arc_spacing=s, cap_axis=axis)","handlingStrategy":"validation","validationCode":"axis = np.asarray(axis, dtype=gs.np_float)\nassert np.linalg.norm(axis) >= gs.EPS, 'cap_axis is (near-)zero'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never feed unnormalized cross products straight into direction parameters.","Fall back to a canonical axis when a computed direction degenerates."],"tags":["genesis","geometry","vector-normalization","argument-validation"],"backgroundTag":"zero-vector-normalization","analyzedSha":"56e4aa5d82bdb83f2e532e4cc364cb69527acb06","analyzedAt":"2026-08-28T15:15:07.922Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}