{"record":{"id":"3554cb060f4e0c66","repo":"3b1b/manim","slug":"cannot-sample-color-from-outside-an-image","errorCode":null,"errorMessage":"Cannot sample color from outside an image","messagePattern":"Cannot sample color from outside an image","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"manimlib/mobject/types/image_mobject.py","lineNumber":71,"sourceCode":"    def set_opacity(self, opacity: float, recurse: bool = True):\n        with self.data.being_written() as data:\n            data[\"opacity\"][:, 0] = resize_with_interpolation(\n                np.array(listify(opacity)),\n                self.get_num_points()\n            )\n        return self\n\n    def set_color(self, color, opacity=None, recurse=None):\n        return self\n\n    def point_to_rgb(self, point: Vect3) -> Vect3:\n        x0, y0 = self.get_corner(UL)[:2]\n        x1, y1 = self.get_corner(DR)[:2]\n        x_alpha = inverse_interpolate(x0, x1, point[0])\n        y_alpha = inverse_interpolate(y0, y1, point[1])\n        if not (0 <= x_alpha <= 1) and (0 <= y_alpha <= 1):\n            # TODO, raise smarter exception\n            raise Exception(\"Cannot sample color from outside an image\")\n\n        pw, ph = self.image.size\n        rgb = self.image.getpixel((\n            int((pw - 1) * x_alpha),\n            int((ph - 1) * y_alpha),\n        ))[:3]\n        return np.array(rgb) / 255\n","sourceCodeStart":53,"sourceCodeEnd":79,"githubUrl":"https://github.com/3b1b/manim/blob/dee01804d47b9f94402d71472674710dcac125b8/manimlib/mobject/types/image_mobject.py#L53-L79","documentation":"Raised by AbstractImageMobject.point_to_rgb (image_mobject.py:71) when the sampled point maps to an x-coordinate outside the image while the y-coordinate is inside. Note the guard reads `if not (0 <= x_alpha <= 1) and (0 <= y_alpha <= 1)` — due to precedence this only fires for out-of-range x with in-range y; a point outside in y (or both) silently clamps and returns an edge pixel instead of raising.","triggerScenarios":"Calling image_mobject.point_to_rgb(point) where point[0] lies left/right of the image's corners but point[1] is vertically within the image; typically when probing with a point produced by an intersection or a cursor position that is off the image.","commonSituations":"Interactive/color-picker scenes sampling colors at arbitrary screen points; using point_to_rgb on points from line_intersections or mouse events without checking they are on the image; relying on the check to catch all out-of-image points and hitting the precedence quirk in manim versions with this source.","solutions":["Before sampling, verify the point is inside the image rectangle: compare against mob.get_corner(UL) and mob.get_corner(DR)","Clamp the point onto the image before calling point_to_rgb if edge-pixel behavior is acceptable","If you maintain a fork/patch, fix the condition to `if not (0 <= x_alpha <= 1 and 0 <= y_alpha <= 1)` so all out-of-bounds points raise"],"exampleFix":"# before\nrgb = img.point_to_rgb(np.array([5.0, 0.5, 0.0]))  # x off-image -> raises\n\n# after\nul, dr = img.get_corner(UL)[:2], img.get_corner(DR)[:2]\nif ul[0] <= p[0] <= dr[0] and dr[1] <= p[1] <= ul[1]:\n    rgb = img.point_to_rgb(p)","handlingStrategy":"validation","validationCode":"def point_on_image(img, p) -> bool:\n    ul, dr = img.get_corner(UL)[:2], img.get_corner(DR)[:2]\n    return ul[0] <= p[0] <= dr[0] and dr[1] <= p[1] <= ul[1]\n\nrgb = img.point_to_rgb(p) if point_on_image(img, p) else DEFAULT_COLOR","typeGuard":null,"tryCatchPattern":"try:\n    rgb = img.point_to_rgb(p)\nexcept Exception:\n    rgb = np.array([0.5, 0.5, 0.5])  # fallback for off-image samples","preventionTips":["Clamp or reject points outside get_corner(UL)/get_corner(DR) before sampling","Do not assume the built-in check covers vertical overflow — guard both axes yourself","For interactive sampling, first hit-test the point against the image's bounding box"],"tags":["manim","image","sampling","bounds-check","color"],"backgroundTag":null,"analyzedSha":"dee01804d47b9f94402d71472674710dcac125b8","analyzedAt":"2026-08-14T19:53:44.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}