{"spec_id":"parallel-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// parallel-basic: Basic Parallel Coordinates Plot\n// Library: highcharts 12.6.0 | JavaScript 22.23.1\n// Quality: 89/100 | Created: 2026-07-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Tiny fixed-seed LCG — the browser has no seeded Math.random().\nlet seed = 42;\nfunction rand() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\nfunction gaussian(mean, std) {\n  const u1 = rand() || 1e-9;\n  const u2 = rand();\n  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n  return mean + z * std;\n}\n\nconst dimensions = [\"Price\", \"Rating\", \"Monthly Sales\", \"Inventory\", \"Return Rate\", \"Discount\"];\nconst formatters = [\n  (v) => `$${v.toFixed(0)}`,\n  (v) => `${v.toFixed(1)}★`,\n  (v) => `${Math.round(v)} units/mo`,\n  (v) => `${Math.round(v)} units`,\n  (v) => `${v.toFixed(1)}%`,\n  (v) => `${v.toFixed(1)}%`,\n];\n\n// Product segments — cheaper items sell in higher volume and carry deeper\n// stock, while premium items command higher price/rating but move slower.\n// Budget items also see more returns and heavier discounting than Premium.\nconst segments = [\n  {\n    name: \"Budget\",\n    means: [25, 3.3, 850, 1200, 8, 18],\n    stds: [8, 0.4, 200, 300, 2, 5],\n  },\n  {\n    name: \"Mid-Range\",\n    means: [75, 4.0, 420, 600, 4, 9],\n    stds: [20, 0.3, 120, 150, 1.2, 3],\n  },\n  {\n    name: \"Premium\",\n    means: [220, 4.6, 95, 150, 1.5, 3],\n    stds: [60, 0.25, 35, 50, 0.6, 1.5],\n  },\n];\nconst clampMin = [5, 1, 10, 10, 0, 0];\nconst clampMax = [Infinity, 5, Infinity, Infinity, 100, 100];\n\nconst samplesPerSegment = 20;\nconst products = [];\nsegments.forEach((seg, segIndex) => {\n  for (let i = 0; i < samplesPerSegment; i++) {\n    products.push({\n      segment: seg.name,\n      segIndex,\n      values: seg.means.map((m, d) =>\n        Math.min(clampMax[d], Math.max(clampMin[d], gaussian(m, seg.stds[d])))\n      ),\n    });\n  }\n});\n\n// Min-max normalize each dimension to [0, 1] so all axes share one scale.\nconst mins = dimensions.map((_, d) => Math.min(...products.map((p) => p.values[d])));\nconst maxs = dimensions.map((_, d) => Math.max(...products.map((p) => p.values[d])));\n\n// --- Interaction: hover a line (or its legend swatch) to spotlight every\n// line in that segment and fade the rest — a lightweight way to trace one\n// group's path across all axes despite 60 overlapping observations.\nconst baseOpacity = 0.4;\nconst dimmedOpacity = 0.06;\nconst highlightOpacity = 0.95;\n\nfunction highlightSegment(chart, name) {\n  chart.series.forEach((s) => {\n    if (s.graph) s.graph.attr({ opacity: s.name === name ? highlightOpacity : dimmedOpacity });\n  });\n}\n\nfunction resetOpacities(chart) {\n  chart.series.forEach((s) => {\n    if (s.graph) s.graph.attr({ opacity: baseOpacity });\n  });\n}\n\nfunction toggleSegmentVisibility(chart, name) {\n  const segmentSeries = chart.series.filter((s) => s.name === name);\n  const anyVisible = segmentSeries.some((s) => s.visible);\n  segmentSeries.forEach((s) => s.setVisible(!anyVisible, false));\n  chart.redraw();\n}\n\nconst seenSegments = new Set();\nconst lineSeries = products.map((p) => {\n  const firstOfSegment = !seenSegments.has(p.segIndex);\n  seenSegments.add(p.segIndex);\n  return {\n    name: p.segment,\n    color: t.palette[p.segIndex],\n    lineWidth: 1.3,\n    opacity: baseOpacity,\n    marker: { enabled: false },\n    showInLegend: firstOfSegment,\n    data: p.values.map((v, d) => ({\n      y: (v - mins[d]) / (maxs[d] - mins[d]),\n      custom: { real: v },\n    })),\n    events: {\n      mouseOver: function () {\n        highlightSegment(this.chart, this.name);\n      },\n      mouseOut: function () {\n        resetOpacities(this.chart);\n      },\n      legendItemClick: function () {\n        toggleSegmentVisibility(this.chart, this.name);\n        return false;\n      },\n    },\n  };\n});\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"parallel-basic · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Product segments across price, rating, sales, inventory, returns & discount (normalized 0–1)\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    categories: dimensions,\n    tickmarkPlacement: \"on\",\n    lineWidth: 0,\n    gridLineWidth: 1.5,\n    gridLineColor: t.inkSoft,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  yAxis: {\n    min: 0,\n    max: 1,\n    tickInterval: 0.25,\n    title: { text: \"Normalized Value\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    gridLineColor: t.grid,\n    gridLineWidth: 1,\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    formatter: function () {\n      const dim = dimensions[this.point.index];\n      const format = formatters[this.point.index];\n      return `<b>${this.series.name}</b><br>${dim}: ${format(this.point.custom.real)}`;\n    },\n  },\n  plotOptions: {\n    series: { animation: false, marker: { enabled: false } },\n  },\n  series: lineSeries,\n});\n"}