{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "plot_spatial3d-intro",
      "metadata": {},
      "source": "# A trajectory and an explicit gridded surface\n\nRun the cells in order. All observations are deterministic synthetic examples. The notebook demonstrates order preserving 3D lines, rectangular surface topology, explicit units and aspect choices.\n"
    },
    {
      "cell_type": "code",
      "id": "plot_spatial3d-1",
      "metadata": {},
      "source": [
        "// Requires the matching spatial-plot-enabled native development runtime.\n",
        "import aner.plot\n",
        "import aner.math\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "id": "plot_spatial3d-2",
      "metadata": {},
      "source": [
        "// A parametric path connects samples in their original order.\n",
        "let pathX = List()\n",
        "let pathY = List()\n",
        "let pathZ = List()\n",
        "for sample in Range(81) {\n",
        "    let t = Float64(sample) * 0.15707963267948966\n",
        "    pathX.append(math.cos(t))\n",
        "    pathY.append(math.sin(t))\n",
        "    pathZ.append(t / 6.283185307179586)\n",
        "}\n",
        "let trajectory = plot.line3d(pathX, pathY, pathZ)\n",
        "    .labels(\"x (m)\", \"y (m)\", \"z (m)\").aspect(\"equal\")\n",
        "    .view(-35.0, 25.0).title(\"A supplied helix connects 81 ordered samples\")\n",
        "    .caption(\"Synthetic parametric path: x=cos(t), y=sin(t), z=t/(2π), 0≤t≤4π. No smoothing, fitted trajectory or physical simulation.\")\n",
        "    .theme(plot.theme(\"light\")).size(940, 640)\n",
        "trajectory.show()\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "id": "plot_spatial3d-3",
      "metadata": {},
      "source": [
        "// Surface axes are strictly increasing; z is row-major, one row per y.\n",
        "let gridX = List()\n",
        "let gridY = List()\n",
        "let gridZ = List()\n",
        "for column in Range(21) { gridX.append(-2.0 + Float64(column) * 0.2) }\n",
        "for row in Range(17) { gridY.append(-2.0 + Float64(row) * 0.25) }\n",
        "for row in Range(17) {\n",
        "    let y = -2.0 + Float64(row) * 0.25\n",
        "    for column in Range(21) {\n",
        "        let x = -2.0 + Float64(column) * 0.2\n",
        "        gridZ.append((x * x - y * y) / 4.0)\n",
        "    }\n",
        "}\n",
        "let saddle = plot.surface3d(x: gridX, y: gridY, z: gridZ)\n",
        "    .labels(\"x (m)\", \"y (m)\", \"Response (arbitrary units)\")\n",
        "    .color_label(\"Response (arbitrary units)\").color_limits(-1.0, 1.0)\n",
        "    .aspect(\"auto\").view(-45.0, 30.0)\n",
        "    .title(\"An explicitly sampled saddle surface\")\n",
        "    .caption(\"Synthetic z=(x²−y²)/4 on a 17×21 rectangular grid. Flat triangles join supplied neighboring vertices; no fitted surface. Auto aspect normalizes each axis independently.\")\n",
        "    .theme(plot.theme(\"light\")).size(1000, 680)\n",
        "saddle.show()\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "plot_spatial3d-export",
      "metadata": {},
      "source": "The line connects supplied samples in order. The surface uses 21 increasing x values, 17 increasing y values and 357 z values; `z[row * x.size + column]` is the corresponding vertex. Each grid cell is split into two flat triangles. The renderer does not infer a triangulation for an irregular point cloud or estimate unsupplied data.\n\nThe helix uses equal aspect because all axes use metres. The saddle uses auto aspect because its response units differ from the horizontal coordinates; this changes display proportions and must not be read as a physical slope. Its color repeats z rather than adding an independent fourth variable. The separate Boulder example supplies temperature as that fourth variable.\n\nRotate, use orthogonal presets or inspect the data table offline. Dense marks and crossing triangles can occlude one another; use 2D projections for precise comparisons. No animation, volume or implicit interpolation is provided.\n\nExport in a **new code cell** with fresh filenames:\n\n```aner\ntrajectory.save(\"spatial-trajectory.html\")\ntrajectory.save(\"spatial-trajectory.svg\")\nsaddle.save(\"spatial-surface.html\")\nsaddle.save(\"spatial-surface.svg\")\n```\n\nThe corresponding `.aner` file runs the same analysis and these exports. All three formats need no map packs or network connection. A matching development runtime and notebook integration are required for execution and interactive output.\n"
    }
  ],
  "metadata": {
    "kernelspec": {
      "name": "aner",
      "display_name": "Aner",
      "language": "aner"
    },
    "language_info": {
      "name": "aner",
      "file_extension": ".aner",
      "mimetype": "text/x-aner"
    },
    "aner": {
      "status": "example",
      "execution_mode": "persistent-session"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
