{"spec_id":"line-3d-trajectory","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nline-3d-trajectory: 3D Line Plot for Trajectory Visualization\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-16\n\"\"\"\n\nimport os\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 - Lorenz attractor trajectory (chaotic system)\nnp.random.seed(42)\n\n# Lorenz system parameters\nsigma = 10.0\nrho = 28.0\nbeta = 8.0 / 3.0\n\n# Generate trajectory using Euler integration\nn_points = 1500\ndt = 0.01\n\nx_traj = np.zeros(n_points)\ny_traj = np.zeros(n_points)\nz_traj = np.zeros(n_points)\n\n# Initial conditions\nx_traj[0], y_traj[0], z_traj[0] = 1.0, 1.0, 1.0\n\nfor i in range(1, n_points):\n    x, y, z = x_traj[i - 1], y_traj[i - 1], z_traj[i - 1]\n    x_traj[i] = x + sigma * (y - x) * dt\n    y_traj[i] = y + (x * (rho - z) - y) * dt\n    z_traj[i] = z + (x * y - beta * z) * dt\n\n# 3D to 2D isometric projection (elevation=20°, azimuth=135° for good view of Lorenz wings)\nelev_rad = np.radians(20)\nazim_rad = np.radians(135)\n\n# Rotation around z-axis (azimuth)\nx_rot = x_traj * np.cos(azim_rad) - y_traj * np.sin(azim_rad)\ny_rot = x_traj * np.sin(azim_rad) + y_traj * np.cos(azim_rad)\n\n# Rotation around x-axis (elevation) and project to 2D\nx_proj = x_rot\nz_proj = y_rot * np.sin(elev_rad) + z_traj * np.cos(elev_rad)\n\n# Create line segments dataframe for mark_rule (each row is a segment)\nsegments = []\nfor i in range(n_points - 1):\n    segments.append(\n        {\n            \"x\": x_proj[i],\n            \"y\": z_proj[i],\n            \"x2\": x_proj[i + 1],\n            \"y2\": z_proj[i + 1],\n            \"time\": i,\n            \"x_orig\": x_traj[i],\n            \"y_orig\": y_traj[i],\n            \"z_orig\": z_traj[i],\n        }\n    )\n\ndf_segments = pd.DataFrame(segments)\n\n# Create trajectory using mark_rule for line segments with color gradient\ntrajectory = (\n    alt.Chart(df_segments)\n    .mark_rule(strokeWidth=2.5, strokeCap=\"round\")\n    .encode(\n        x=alt.X(\"x:Q\", axis=alt.Axis(title=\"X-Y Projection (Horizontal)\", labelFontSize=18, titleFontSize=22)),\n        y=alt.Y(\"y:Q\", axis=alt.Axis(title=\"Z Projection (Vertical)\", labelFontSize=18, titleFontSize=22)),\n        x2=\"x2:Q\",\n        y2=\"y2:Q\",\n        color=alt.Color(\n            \"time:Q\",\n            scale=alt.Scale(scheme=\"viridis\"),\n            legend=alt.Legend(title=\"Time Step\", titleFontSize=20, labelFontSize=16, orient=\"right\"),\n        ),\n        tooltip=[\n            alt.Tooltip(\"x_orig:Q\", title=\"X\", format=\".2f\"),\n            alt.Tooltip(\"y_orig:Q\", title=\"Y\", format=\".2f\"),\n            alt.Tooltip(\"z_orig:Q\", title=\"Z\", format=\".2f\"),\n            alt.Tooltip(\"time:Q\", title=\"Time Step\"),\n        ],\n    )\n)\n\n# Add pan and zoom interactivity\npan_zoom = alt.selection_interval(bind=\"scales\", encodings=[\"x\", \"y\"])\n\n# Final chart\nchart = (\n    trajectory.add_params(pan_zoom)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(text=\"line-3d-trajectory · altair · anyplot.ai\", fontSize=28, color=INK),\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_title(color=INK)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}