{"spec_id":"polar-line","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// polar-line: Polar Line Plot\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-05\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Average household electricity demand (kWh) by hour of day. Weekday shows the\n// classic double peak (commute-driven morning ramp, evening cooking/lighting);\n// weekend is flatter with one broad midday-into-evening bump instead of a sharp\n// morning spike.\nconst weekdayKwh = [\n  0.32, 0.28, 0.26, 0.25, 0.27, 0.34, 0.52, 0.88, 0.95, 0.62, 0.48, 0.44, 0.42,\n  0.4, 0.38, 0.41, 0.5, 0.68, 1.05, 1.22, 1.1, 0.82, 0.58, 0.42,\n];\nconst weekendKwh = [\n  0.4, 0.36, 0.33, 0.31, 0.3, 0.32, 0.38, 0.48, 0.58, 0.66, 0.72, 0.78, 0.82,\n  0.8, 0.76, 0.74, 0.78, 0.88, 0.98, 1.02, 0.94, 0.8, 0.62, 0.48,\n];\n\n// --- Polar -> Cartesian ------------------------------------------------------\n// Core Highcharts has no `chart.polar` (that lives in the unloaded highcharts-more\n// module — see prompts/library/highcharts.md \"No add-on modules\"). Instead of\n// returning NOT_FEASIBLE, the polar geometry is built by hand: each (hour, kWh)\n// reading is converted to an (x, y) pair and drawn as a genuine line series on a\n// hidden, equal-range Cartesian grid, with the angular/radial gridlines drawn to\n// the same scale via the renderer — real coordinates, not a picture of a polar\n// chart.\nconst HOURS = 24;\nconst AXIS_MAX = 1.3; // kWh — outer ring radius, headroom over the 1.22 peak\nconst RADIUS_TICKS = [0.3, 0.6, 0.9, 1.2];\n\nfunction polarPoint(radius, hour) {\n  const theta = (hour / HOURS) * 2 * Math.PI; // 0 = top (midnight), clockwise\n  return { x: radius * Math.sin(theta), y: radius * Math.cos(theta) };\n}\n\nfunction seriesData(values, name) {\n  const points = values.map((kwh, hour) => ({\n    ...polarPoint(kwh, hour),\n    custom: { hour, kwh },\n  }));\n  points.push({ ...points[0] }); // close the loop: repeat hour 0 at the end\n  return { name, data: points };\n}\n\nconst weekday = seriesData(weekdayKwh, \"Weekday\");\nconst weekend = seriesData(weekendKwh, \"Weekend\");\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    margin: [160, 140, 120, 140],\n    style: { fontFamily: \"inherit\" },\n    events: {\n      render() {\n        const chart = this;\n        if (chart.polarGrid) chart.polarGrid.destroy();\n        const group = chart.renderer.g(\"polar-grid\").add();\n        group.attr({ zIndex: 1 });\n        const xAxis = chart.xAxis[0];\n        const yAxis = chart.yAxis[0];\n        const cx = xAxis.toPixels(0);\n        const cy = yAxis.toPixels(0);\n\n        // Concentric radius rings, labeled along the 15:00 spoke — at that hour\n        // both series sit comfortably clear of every ring, unlike the 01:00\n        // spoke where the 0.3 ring used to crowd the lines (values ~0.28-0.36\n        // kWh at hour 1, right on top of the ring).\n        RADIUS_TICKS.forEach((r) => {\n          const rPix = Math.abs(xAxis.toPixels(r) - cx);\n          chart.renderer\n            .circle(cx, cy, rPix)\n            .attr({ fill: \"none\", stroke: t.grid, \"stroke-width\": 1 })\n            .add(group);\n          const labelAt = polarPoint(r, 15);\n          chart.renderer\n            .text(`${r.toFixed(1)} kWh`, xAxis.toPixels(labelAt.x) + 4, yAxis.toPixels(labelAt.y) - 4)\n            .css({ color: t.inkSoft, fontSize: \"12px\" })\n            .add(group);\n        });\n\n        // Radial spokes + hour labels every 3 hours.\n        for (let hour = 0; hour < HOURS; hour += 3) {\n          const edge = polarPoint(AXIS_MAX, hour);\n          chart.renderer\n            .path([\"M\", cx, cy, \"L\", xAxis.toPixels(edge.x), yAxis.toPixels(edge.y)])\n            .attr({ stroke: t.grid, \"stroke-width\": 1 })\n            .add(group);\n\n          const labelAt = polarPoint(AXIS_MAX * 1.1, hour);\n          chart.renderer\n            .text(`${String(hour).padStart(2, \"0\")}:00`, xAxis.toPixels(labelAt.x), yAxis.toPixels(labelAt.y))\n            .attr({ align: \"center\" })\n            .css({ color: t.inkSoft, fontSize: \"14px\" })\n            .add(group);\n        }\n\n        chart.polarGrid = group;\n      },\n    },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"Hourly Energy Demand · polar-line · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"20px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Average U.S. household electricity use by hour, weekday vs. weekend\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: { min: -AXIS_MAX, max: AXIS_MAX, visible: false },\n  yAxis: { min: -AXIS_MAX, max: AXIS_MAX, visible: false, title: { text: null } },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    backgroundColor: t.elevatedBg,\n    style: { color: t.ink },\n    formatter() {\n      const { hour, kwh } = this.point.custom;\n      return `<b>${this.series.name}</b><br>${String(hour).padStart(2, \"0\")}:00 — ${kwh.toFixed(2)} kWh`;\n    },\n  },\n  plotOptions: {\n    series: {\n      animation: false,\n      lineWidth: 2.5,\n      marker: { radius: 6, lineWidth: 0 },\n    },\n  },\n  series: [weekday, weekend],\n});\n"}