{"spec_id":"contour-filled","library":"altair","language":"python","code":"\"\"\" anyplot.ai\ncontour-filled: Filled Contour Plot\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-11\n\"\"\"\n\nimport os\nimport sys\n\n# Remove script directory from sys.path to avoid importing local altair.py\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nif script_dir in sys.path:\n    sys.path.remove(script_dir)\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Data - 2D Gaussian peaks for filled contour visualization\nnp.random.seed(42)\nn_points = 80\nx = np.linspace(-3, 3, n_points)\ny = np.linspace(-3, 3, n_points)\nX, Y = np.meshgrid(x, y)\n\n# Create surface with multiple peaks and valleys\nZ = (\n    1.5 * np.exp(-((X - 1) ** 2 + (Y - 1) ** 2) / 0.8)\n    + 1.2 * np.exp(-((X + 1) ** 2 + (Y + 0.5) ** 2) / 1.0)\n    - 0.6 * np.exp(-((X) ** 2 + (Y - 1.5) ** 2) / 0.6)\n    + 0.2 * np.sin(X * 2) * np.cos(Y * 2)\n)\n\n# Create contour levels\nn_levels = 12\nz_min, z_max = Z.min(), Z.max()\nlevels = np.linspace(z_min, z_max, n_levels + 1)\n\n# Bin z-values and map to level centers for color mapping\nZ_binned = np.digitize(Z, levels) - 1\nZ_binned = np.clip(Z_binned, 0, n_levels - 1)\nlevel_centers = (levels[:-1] + levels[1:]) / 2\nZ_discrete = level_centers[Z_binned]\n\n# Create rectangle grid for filled contours\nstep = x[1] - x[0]\nhalf_step = step / 2\n\ndf = pd.DataFrame(\n    {\n        \"x\": X.ravel() - half_step,\n        \"x2\": X.ravel() + half_step,\n        \"y\": Y.ravel() - half_step,\n        \"y2\": Y.ravel() + half_step,\n        \"z\": Z_discrete.ravel(),\n    }\n)\n\n# Filled contour using mark_rect\nfilled_contour = (\n    alt.Chart(df)\n    .mark_rect(stroke=\"none\")\n    .encode(\n        x=alt.X(\n            \"x:Q\",\n            title=\"X Coordinate\",\n            scale=alt.Scale(domain=[-3.1, 3.1]),\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22, tickCount=7),\n        ),\n        x2=\"x2:Q\",\n        y=alt.Y(\n            \"y:Q\",\n            title=\"Y Coordinate\",\n            scale=alt.Scale(domain=[-3.1, 3.1]),\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22, tickCount=7),\n        ),\n        y2=\"y2:Q\",\n        color=alt.Color(\n            \"z:Q\",\n            title=\"Intensity\",\n            scale=alt.Scale(scheme=\"viridis\"),\n            legend=alt.Legend(\n                titleFontSize=20,\n                labelFontSize=16,\n                gradientLength=400,\n                gradientThickness=25,\n                fillColor=ELEVATED_BG,\n                strokeColor=INK_SOFT,\n            ),\n        ),\n    )\n)\n\n\n# Create contour line overlay\ndef find_contours(Z, level, x_coords, y_coords):\n    \"\"\"Extract contour lines using marching squares algorithm.\"\"\"\n    rows, cols = Z.shape\n    segments = []\n\n    for i in range(rows - 1):\n        for j in range(cols - 1):\n            z00, z01, z10, z11 = Z[i, j], Z[i, j + 1], Z[i + 1, j], Z[i + 1, j + 1]\n            cell = [z00, z01, z10, z11]\n            case = sum([1 << k for k, v in enumerate(cell) if v >= level])\n\n            if case in (0, 15):\n                continue\n\n            x0, x1 = x_coords[j], x_coords[j + 1]\n            y0, y1 = y_coords[i], y_coords[i + 1]\n\n            def interp(v1, v2, c1, c2):\n                if abs(v2 - v1) < 1e-10:\n                    return (c1 + c2) / 2\n                t = (level - v1) / (v2 - v1)\n                return c1 + t * (c2 - c1)\n\n            edges = {\n                \"top\": (interp(z00, z01, x0, x1), y0),\n                \"bottom\": (interp(z10, z11, x0, x1), y1),\n                \"left\": (x0, interp(z00, z10, y0, y1)),\n                \"right\": (x1, interp(z01, z11, y0, y1)),\n            }\n\n            cases = {\n                1: [(\"left\", \"top\")],\n                2: [(\"top\", \"right\")],\n                3: [(\"left\", \"right\")],\n                4: [(\"bottom\", \"left\")],\n                5: [(\"top\", \"bottom\")],\n                6: [(\"top\", \"left\"), (\"bottom\", \"right\")]\n                if (z00 + z11) / 2 >= level\n                else [(\"top\", \"right\"), (\"bottom\", \"left\")],\n                7: [(\"bottom\", \"right\")],\n                8: [(\"right\", \"bottom\")],\n                9: [(\"left\", \"bottom\"), (\"right\", \"top\")]\n                if (z00 + z11) / 2 >= level\n                else [(\"left\", \"top\"), (\"right\", \"bottom\")],\n                10: [(\"top\", \"bottom\")],\n                11: [(\"left\", \"bottom\")],\n                12: [(\"left\", \"right\")],\n                13: [(\"top\", \"right\")],\n                14: [(\"left\", \"top\")],\n            }\n\n            for e1, e2 in cases.get(case, []):\n                segments.append((edges[e1], edges[e2]))\n\n    return segments\n\n\ncontour_lines_data = []\ncontour_levels_subset = levels[2:-2:2]\n\nfor idx, level_val in enumerate(contour_levels_subset):\n    segments = find_contours(Z, level_val, x, y)\n    for seg_idx, (p1, p2) in enumerate(segments):\n        contour_id = f\"L{idx}_S{seg_idx}\"\n        contour_lines_data.append({\"x\": p1[0], \"y\": p1[1], \"order\": 0, \"contour_id\": contour_id})\n        contour_lines_data.append({\"x\": p2[0], \"y\": p2[1], \"order\": 1, \"contour_id\": contour_id})\n\ncontour_df = pd.DataFrame(contour_lines_data)\n\n# Contour line overlay with theme-adaptive color\ncontour_color = \"#2A2A25\" if THEME == \"light\" else \"#D5D4CC\"\ncontour_overlay = (\n    alt.Chart(contour_df)\n    .mark_line(strokeWidth=1.2, opacity=0.4)\n    .encode(\n        x=alt.X(\"x:Q\", scale=alt.Scale(domain=[-3.1, 3.1])),\n        y=alt.Y(\"y:Q\", scale=alt.Scale(domain=[-3.1, 3.1])),\n        order=\"order:O\",\n        detail=\"contour_id:N\",\n        color=alt.value(contour_color),\n    )\n)\n\n# Combine layers with theme-adaptive styling\nchart = (\n    alt.layer(filled_contour, contour_overlay)\n    .properties(\n        width=1600,\n        height=900,\n        title=alt.Title(text=\"contour-filled · altair · anyplot.ai\", fontSize=28, anchor=\"middle\"),\n        background=PAGE_BG,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)\n    .configure_axis(domainColor=INK_SOFT, tickColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n    .configure_title(color=INK)\n)\n\n# Save PNG and HTML with theme suffix\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}