{"spec_id":"wireframe-3d-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nwireframe-3d-basic: Basic 3D Wireframe Plot\nLibrary: letsplot 4.11.0 | Python 3.13.14\nQuality: 87/100 | Updated: 2026-08-04\n\"\"\"\n\nimport os\nimport shutil\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# First series color\nBRAND = \"#009E73\"\n\n# Generate 3D surface data (ripple function)\nx = np.linspace(-6, 6, 28)\ny = np.linspace(-6, 6, 28)\nX, Y = np.meshgrid(x, y)\nZ = np.sin(np.sqrt(X**2 + Y**2))\n\nX_MIN, X_MAX = float(x.min()), float(x.max())\nY_MIN, Y_MAX = float(y.min()), float(y.max())\nZ_MIN, Z_MAX = float(Z.min()), float(Z.max())\nFLOOR = Z_MIN - 0.6  # reference plane below the surface, for the axis frame\nTICK = 0.3\n# Pull the axis-frame corner outward from the data extent so the frame reads\n# as a detached reference box instead of colliding with the mesh's own front vertex\nAXIS_X = X_MIN - 2.0\nAXIS_Y = Y_MIN - 2.0\n\n\n# Isometric projection from 3D to 2D\ndef isometric_project(x_3d, y_3d, z_3d):\n    x_iso = x_3d - y_3d\n    y_iso = (x_3d + y_3d) * 0.5 + z_3d\n    return x_iso, y_iso\n\n\n# Wireframe mesh, colored by height so elevation reads as a second visual cue\nmesh = []\n\n# X-direction lines (constant y index)\nfor j in range(X.shape[1]):\n    x_2d, y_2d = isometric_project(X[:, j], Y[:, j], Z[:, j])\n    for i in range(len(x_2d)):\n        mesh.append({\"x\": x_2d[i], \"y\": y_2d[i], \"z\": Z[i, j], \"line\": f\"x_{j}\", \"order\": i})\n\n# Y-direction lines (constant x index)\nfor i in range(X.shape[0]):\n    x_2d, y_2d = isometric_project(X[i, :], Y[i, :], Z[i, :])\n    for j in range(len(x_2d)):\n        mesh.append({\"x\": x_2d[j], \"y\": y_2d[j], \"z\": Z[i, j], \"line\": f\"y_{i}\", \"order\": j})\n\nmesh_df = pd.DataFrame(mesh)\n\n# Axis frame: X/Y ground-plane edges plus a Z ladder, all drawn in the same\n# isometric-projected space as the mesh (lets-plot has no native 3D axes)\naxis_segs = []\n\n\ndef seg3(p0, p1):\n    a = isometric_project(*p0)\n    b = isometric_project(*p1)\n    axis_segs.append({\"x\": a[0], \"y\": a[1], \"xend\": b[0], \"yend\": b[1]})\n\n\nseg3((AXIS_X, AXIS_Y, FLOOR), (X_MAX, AXIS_Y, FLOOR))  # X-axis line\nseg3((AXIS_X, AXIS_Y, FLOOR), (AXIS_X, Y_MAX, FLOOR))  # Y-axis line\nseg3((AXIS_X, AXIS_Y, FLOOR), (AXIS_X, AXIS_Y, Z_MAX + 0.5))  # Z-axis line\n\nX_TICKS = np.linspace(X_MIN, X_MAX, 5)\nY_TICKS = np.linspace(Y_MIN, Y_MAX, 5)\nZ_TICKS = np.linspace(Z_MIN, Z_MAX, 5)\n\nfor xv in X_TICKS:\n    seg3((xv, AXIS_Y, FLOOR), (xv, AXIS_Y, FLOOR - TICK))\nfor yv in Y_TICKS:\n    seg3((AXIS_X, yv, FLOOR), (AXIS_X, yv, FLOOR - TICK))\nfor zv in Z_TICKS:\n    seg3((AXIS_X, AXIS_Y, zv), (AXIS_X - TICK, AXIS_Y, zv))\n\naxis_df = pd.DataFrame(axis_segs)\n\n# Tick labels\ntick_labels = []\nfor xv in X_TICKS:\n    lx, ly = isometric_project(xv, AXIS_Y, FLOOR - TICK * 2)\n    tick_labels.append({\"x\": lx, \"y\": ly, \"label\": f\"{xv:.0f}\"})\nfor yv in Y_TICKS:\n    lx, ly = isometric_project(AXIS_X, yv, FLOOR - TICK * 2)\n    tick_labels.append({\"x\": lx, \"y\": ly, \"label\": f\"{yv:.0f}\"})\nfor zv in Z_TICKS:\n    lx, ly = isometric_project(AXIS_X - TICK * 2, AXIS_Y, zv)\n    tick_labels.append({\"x\": lx, \"y\": ly, \"label\": f\"{zv:.1f}\"})\ntick_label_df = pd.DataFrame(tick_labels)\n\n# Axis titles (X / Y / Z), placed just past the last tick on each axis\nax_x, ax_y = isometric_project(X_MAX + 0.8, AXIS_Y, FLOOR - TICK * 1.6)\nay_x, ay_y = isometric_project(AXIS_X, Y_MAX + 0.8, FLOOR - TICK * 1.6)\naz_x, az_y = isometric_project(AXIS_X - TICK * 1.6, AXIS_Y, Z_MAX + 0.7)\naxis_title_df = pd.DataFrame(\n    [{\"x\": ax_x, \"y\": ax_y, \"label\": \"X\"}, {\"x\": ay_x, \"y\": ay_y, \"label\": \"Y\"}, {\"x\": az_x, \"y\": az_y, \"label\": \"Z\"}]\n)\n\n# Create plot with wireframe lines, colored by height, plus a minimal axis frame\nplot = (\n    ggplot()\n    + geom_path(aes(x=\"x\", y=\"y\", group=\"line\", color=\"z\"), data=mesh_df, size=0.6, alpha=0.85)\n    + scale_color_gradient(low=BRAND, high=\"#4467A3\", guide=\"none\")\n    + geom_segment(aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\"), data=axis_df, color=INK_SOFT, size=0.6, alpha=0.85)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=tick_label_df, color=INK_SOFT, size=3.2)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=axis_title_df, color=INK, size=4.2, fontface=\"bold\")\n    + ggsize(800, 450)\n    + theme_void()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_title=element_text(size=16, color=INK, hjust=0.5),\n    )\n    + labs(title=\"wireframe-3d-basic · letsplot · anyplot.ai\")\n)\n\n# Setup and save\nLetsPlot.setup_html()\n\nggsave(plot, f\"plot-{THEME}.png\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\")\n\n# Move files from lets-plot-images to current directory\nfor ext in [\"png\", \"html\"]:\n    src = f\"lets-plot-images/plot-{THEME}.{ext}\"\n    dst = f\"plot-{THEME}.{ext}\"\n    if os.path.exists(src):\n        shutil.move(src, dst)\n"}