{"spec_id":"surface-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nsurface-basic: Basic 3D Surface Plot\nLibrary: bokeh 3.9.1 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-06-16\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColorBar, LinearColorMapper\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\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\n# Imprint sequential colormap (brand green -> blue), the only allowed\n# single-polarity continuous ramp. Build a 256-stop gradient by interpolating\n# the two endpoints in RGB space.\ngreen_rgb = np.array([0x00, 0x9E, 0x73])\nblue_rgb = np.array([0x44, 0x67, 0xA3])\nIMPRINT_SEQ256 = [\n    \"#{:02X}{:02X}{:02X}\".format(*np.round(green_rgb + (blue_rgb - green_rgb) * t / 255).astype(int))\n    for t in range(256)\n]\n\n# Data - a smooth synthetic terrain (sum of Gaussian hills) over a 10x10 km tile.\n# Single-polarity elevation in metres -> imprint_seq colormap.\nn_points = 45\neasting = np.linspace(0, 10, n_points)\nnorthing = np.linspace(0, 10, n_points)\nX, Y = np.meshgrid(easting, northing)\n\n# Each hill: (centre_x, centre_y, peak_height_m, spread); the last is a basin.\nhills = [(3.0, 3.2, 720, 1.7), (7.0, 6.4, 540, 1.9), (5.2, 8.4, 360, 1.3), (8.4, 2.4, 300, 1.4), (4.6, 5.6, -220, 1.2)]\nelevation = 480 + sum(\n    height * np.exp(-((X - cx) ** 2 + (Y - cy) ** 2) / (2 * spread**2)) for cx, cy, height, spread in hills\n)\n\n# Vertical geometry: normalise elevation into the x/y coordinate range so the\n# isometric projection isn't crushed or exaggerated. Colour still uses metres.\nz_visual = (elevation - elevation.min()) / (elevation.max() - elevation.min()) * 5.0\n\n# 3D -> 2D isometric projection (elevation 28 deg, azimuth 45 deg)\nelev_rad = np.radians(28)\nazim_rad = np.radians(45)\n\nXc, Yc = X - 5.0, Y - 5.0\nX_rot = Xc * np.cos(azim_rad) - Yc * np.sin(azim_rad)\nY_rot = Xc * np.sin(azim_rad) + Yc * np.cos(azim_rad)\nX_proj = X_rot\nZ_proj = Y_rot * np.sin(elev_rad) + z_visual * np.cos(elev_rad)\n\n# Build one quad per grid cell, with depth + colour from the cell average\nelev_min, elev_max = elevation.min(), elevation.max()\nquads = []\nfor i in range(n_points - 1):\n    for j in range(n_points - 1):\n        xs = [X_proj[i, j], X_proj[i, j + 1], X_proj[i + 1, j + 1], X_proj[i + 1, j]]\n        ys = [Z_proj[i, j], Z_proj[i, j + 1], Z_proj[i + 1, j + 1], Z_proj[i + 1, j]]\n        avg_elev = (elevation[i, j] + elevation[i, j + 1] + elevation[i + 1, j + 1] + elevation[i + 1, j]) / 4\n        depth = (Y_rot[i, j] + Y_rot[i, j + 1] + Y_rot[i + 1, j + 1] + Y_rot[i + 1, j]) / 4\n        quads.append((depth, xs, ys, avg_elev))\n\n# Painter's algorithm: draw back (far) quads first\nquads.sort(key=lambda q: q[0], reverse=True)\nquad_xs = [q[1] for q in quads]\nquad_ys = [q[2] for q in quads]\nquad_idx = [int((q[3] - elev_min) / (elev_max - elev_min) * 255) for q in quads]\nquad_fill = [IMPRINT_SEQ256[max(0, min(255, k))] for k in quad_idx]\n\n# Plot\np = figure(\n    width=3200,\n    height=1800,\n    title=\"surface-basic · python · bokeh · anyplot.ai\",\n    toolbar_location=None,\n    tools=\"\",\n    min_border_top=110,\n    min_border_left=60,\n    min_border_bottom=80,\n    min_border_right=80,\n)\n\n# Surface: filled quads with subtle edges to read the mesh and aid depth\np.patches(xs=quad_xs, ys=quad_ys, fill_color=quad_fill, line_color=INK_SOFT, line_alpha=0.22, line_width=0.6)\n\n# Floating, descriptive axis labels (a projected 3D surface has no flat axes)\nx_lo, x_hi = min(min(v) for v in quad_xs), max(max(v) for v in quad_xs)\ny_lo, y_hi = min(min(v) for v in quad_ys), max(max(v) for v in quad_ys)\nx_pad = (x_hi - x_lo) * 0.16\n\np.text(\n    x=[x_hi + x_pad * 0.30],\n    y=[y_lo + (y_hi - y_lo) * 0.30],\n    text=[\"Easting (km)\"],\n    text_font_size=\"38pt\",\n    text_color=INK_SOFT,\n    text_align=\"center\",\n    text_baseline=\"middle\",\n    angle=np.radians(28),\n)\np.text(\n    x=[x_lo - x_pad * 0.30],\n    y=[y_lo + (y_hi - y_lo) * 0.30],\n    text=[\"Northing (km)\"],\n    text_font_size=\"38pt\",\n    text_color=INK_SOFT,\n    text_align=\"center\",\n    text_baseline=\"middle\",\n    angle=np.radians(-28),\n)\np.text(\n    x=[x_lo - x_pad * 0.55],\n    y=[(y_lo + y_hi) / 2],\n    text=[\"Elevation (m)\"],\n    text_font_size=\"38pt\",\n    text_color=INK_SOFT,\n    text_align=\"center\",\n    text_baseline=\"middle\",\n    angle=np.radians(90),\n)\n\n# Colorbar for the elevation scale\ncolor_mapper = LinearColorMapper(palette=IMPRINT_SEQ256, low=elev_min, high=elev_max)\ncolor_bar = ColorBar(\n    color_mapper=color_mapper,\n    width=42,\n    padding=18,\n    title=\"Elevation (m)\",\n    title_text_font_size=\"34pt\",\n    title_text_color=INK,\n    title_text_font_style=\"normal\",\n    title_standoff=16,\n    major_label_text_font_size=\"30pt\",\n    major_label_text_color=INK_SOFT,\n    major_tick_line_color=INK_SOFT,\n    background_fill_color=ELEVATED_BG,\n    background_fill_alpha=0.0,\n)\np.add_layout(color_bar, \"right\")\n\n# Style - clean, frameless projection\np.x_range.range_padding = 0.10\np.y_range.range_padding = 0.12\np.title.text_font_size = \"50pt\"\np.title.text_color = INK\np.xaxis.visible = False\np.yaxis.visible = False\np.xgrid.grid_line_color = None\np.ygrid.grid_line_color = None\np.outline_line_color = None\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\n\n# Save - interactive HTML (catalog artifact) + headless-Chrome screenshot\noutput_file(f\"plot-{THEME}.html\", title=\"surface-basic · python · bokeh · anyplot.ai\")\nsave(p)\n\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\n\n# Headless Chrome reserves some window chrome, so the inner viewport ends up\n# shorter than the requested window. Grow the window by that delta so the inner\n# viewport is exactly W x H and the screenshot captures the full bokeh canvas.\ninner_w, inner_h = driver.execute_script(\"return [window.innerWidth, window.innerHeight]\")\ndriver.set_window_size(W + (W - inner_w), H + (H - inner_h))\n\ntime.sleep(3)  # let bokeh's JS render the canvas\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}