"""Latent sampler, Pillow renderer, and analytic arm for fragmented_circle_center.""" from __future__ import annotations import copy import math import random from PIL import Image, ImageDraw COLORS: tuple[str, ...] = ("red", "blue", "green", "orange") RGB = { "red": (205, 67, 85), "blue": (45, 103, 210), "green": (38, 151, 105), "orange": (232, 132, 39), } BACKGROUND = (248, 247, 242) DARK = (40, 48, 58) QUARANTINE_MARGIN_PX = 18.0 def sample_scene(seed: int) -> dict: """Sample a balanced scene. Geometry is shared within each consecutive group of four seeds, while the answer color rotates. Thus answer identity is exactly counterbalanced against center, radius, arc layout, dot positions, size, count, and ink. """ geometry_id = int(seed) // 4 answer_index = int(seed) % len(COLORS) rng = random.Random(0xC1A0 + geometry_id * 104729) width = height = 512 radius = rng.randint(116, 142) pad = radius + 24 cx = rng.randint(pad, width - pad) cy = rng.randint(pad, height - pad) # Fifteen globally distributed arclets; no local fragment identifies the # center by itself. A random phase and jitter prevent a fixed template. phase = rng.uniform(0.0, 24.0) arc_intervals: list[list[float]] = [] for index in range(15): start = (phase + index * 24.0 + rng.uniform(-2.2, 2.2)) % 360.0 span = rng.uniform(11.0, 15.0) end = start + span if end <= 360.0: arc_intervals.append([round(start, 4), round(end, 4)]) else: arc_intervals.append([round(start, 4), 360.0]) arc_intervals.append([0.0, round(end - 360.0, 4)]) # The first dot position is the true center. The nearest decoy cycles over # three margin bands independently of answer color. angle0 = rng.uniform(0.0, 2.0 * math.pi) near_distance = (23.0, 36.0, 54.0)[geometry_id % 3] distances = (near_distance, rng.uniform(64.0, 76.0), rng.uniform(78.0, 91.0)) decoy_xy: list[list[float]] = [] for index, distance in enumerate(distances): angle = angle0 + index * 2.0 * math.pi / 3.0 + rng.uniform(-0.10, 0.10) decoy_xy.append( [round(cx + distance * math.cos(angle), 4), round(cy + distance * math.sin(angle), 4)] ) answer_color = COLORS[answer_index] other_colors = [COLORS[(answer_index + offset) % 4] for offset in (1, 2, 3)] markers = [{"color": answer_color, "xy": [float(cx), float(cy)]}] markers.extend( {"color": color, "xy": xy} for color, xy in zip(other_colors, decoy_xy, strict=True) ) # Order has no rendering or semantic meaning and supplies a tested latent alias. rng.shuffle(markers) return { "width": width, "height": height, "center": [float(cx), float(cy)], "radius": float(radius), "arc_intervals": arc_intervals, "markers": markers, "stroke_width": 8, "marker_radius": 11, "background": list(BACKGROUND), "dark": list(DARK), } def render(scene: dict) -> Image.Image: """Render only visible evidence, with deterministic supersampled antialiasing.""" scale = 4 width = int(scene["width"]) height = int(scene["height"]) background = tuple(scene["background"]) dark = tuple(scene["dark"]) image = Image.new("RGB", (width * scale, height * scale), background) draw = ImageDraw.Draw(image) cx, cy = scene["center"] radius = float(scene["radius"]) box = [ int(round((cx - radius) * scale)), int(round((cy - radius) * scale)), int(round((cx + radius) * scale)), int(round((cy + radius) * scale)), ] stroke = int(scene["stroke_width"]) * scale for start, end in scene["arc_intervals"]: draw.arc(box, start=float(start), end=float(end), fill=dark, width=stroke) marker_radius = int(scene["marker_radius"]) * scale halo_radius = marker_radius + 3 * scale for marker in scene["markers"]: mx, my = marker["xy"] px = int(round(mx * scale)) py = int(round(my * scale)) draw.ellipse( [px - halo_radius, py - halo_radius, px + halo_radius, py + halo_radius], fill=background, ) draw.ellipse( [px - marker_radius, py - marker_radius, px + marker_radius, py + marker_radius], fill=RGB[marker["color"]], ) return image.resize((width, height), Image.Resampling.LANCZOS) def analytic_gold(scene: dict) -> str: """Select the marker nearest the declared circle center from visible fields.""" cx, cy = scene["center"] markers = scene["markers"] winner = min(markers, key=lambda item: math.hypot(item["xy"][0] - cx, item["xy"][1] - cy)) return str(winner["color"]) def margin(scene: dict) -> float: """Top-two gap in candidate distances to the implied center, in pixels.""" cx, cy = scene["center"] distances = sorted( math.hypot(marker["xy"][0] - cx, marker["xy"][1] - cy) for marker in scene["markers"] ) return float(distances[1] - distances[0]) def is_quarantined(scene: dict) -> bool: return margin(scene) < QUARANTINE_MARGIN_PX def latent_symmetries(scene: dict) -> list[tuple[str, dict]]: """Reverse unordered same-color arclets; pixels and gold are invariant. Marker order is intentionally left fixed because background halos can overlap in the near-margin band, making marker draw order raster-observable. """ twin = copy.deepcopy(scene) twin["arc_intervals"] = list(reversed(twin["arc_intervals"])) return [("reverse_unordered_arc_list", twin)]