{"spec_id":"dendrogram-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// dendrogram-basic: Basic Dendrogram\n// Library: highcharts 12.6.0 | JavaScript 22.22.3\n// Quality: 91/100 | Created: 2026-06-18\n//# anyplot-orientation: landscape\n// anyplot.ai\n// dendrogram-basic: Basic Dendrogram\n// Library: Highcharts 12.6.0 | Node 22\n// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license)\n// Quality: pending | Created: 2026-06-18\n\nconst t = window.ANYPLOT_TOKENS;\nconst GREEN = t.palette[0];  // #009E73 — data-science / scripting cluster\nconst BLUE  = t.palette[2];  // #4467A3 — systems / web cluster\n\nconst leafLabels = [\"Python\", \"R\", \"Julia\", \"MATLAB\", \"JavaScript\", \"TypeScript\", \"Java\", \"C++\"];\n\n// Pre-computed agglomerative clustering of programming languages by paradigm features.\n// Each row: [leftX, leftH, rightX, rightH, mergeH, branchColor]\n//   leftX / rightX  — x-centre of the child (leaf index or midpoint of a prior merge)\n//   leftH / rightH  — merge height of that child (0 for leaves)\n//   mergeH          — linkage distance at which the two children join\nconst merges = [\n    [0,   0,    1,   0,    1.1, GREEN],          // Python  + R\n    [2,   0,    3,   0,    1.3, GREEN],          // Julia   + MATLAB\n    [0.5, 1.1,  2.5, 1.3,  2.5, GREEN],         // scripting + scientific → data-science cluster\n    [4,   0,    5,   0,    0.8, BLUE],           // JavaScript + TypeScript\n    [6,   0,    7,   0,    1.5, BLUE],           // Java    + C++\n    [4.5, 0.8,  6.5, 1.5,  2.8, BLUE],          // web     + systems → systems cluster\n    [1.5, 2.5,  5.5, 2.8,  5.0, t.inkSoft],     // root merge\n];\n\nHighcharts.chart(\"container\", {\n    chart: {\n        backgroundColor: \"transparent\",\n        animation: false,\n        marginLeft:   90,\n        marginRight:  30,\n        marginBottom: 100,\n        marginTop:    80,\n        style: { fontFamily: \"inherit\" },\n        events: {\n            render: function () {\n                const c = this;\n\n                // Destroy and recreate the renderer group on every render pass\n                if (c._dg) c._dg.destroy();\n                const g = c.renderer.g(\"dg\").add();\n                c._dg = g;\n\n                const xa = c.xAxis[0];\n                const ya = c.yAxis[0];\n                const lw = 2.5;\n\n                // Draw each merge as three line segments: horizontal bar + two vertical drops\n                merges.forEach(([lx, lh, rx, rh, mh, col]) => {\n                    const x1 = xa.toPixels(lx, false);\n                    const x2 = xa.toPixels(rx, false);\n                    const ym = ya.toPixels(mh, false);\n                    const yl = ya.toPixels(lh, false);\n                    const yr = ya.toPixels(rh, false);\n\n                    // Horizontal bar at merge height\n                    c.renderer.path([\"M\", x1, ym, \"L\", x2, ym])\n                        .attr({ stroke: col, \"stroke-width\": lw, zIndex: 5 })\n                        .add(g);\n                    // Left vertical drop\n                    c.renderer.path([\"M\", x1, ym, \"L\", x1, yl])\n                        .attr({ stroke: col, \"stroke-width\": lw, zIndex: 5 })\n                        .add(g);\n                    // Right vertical drop\n                    c.renderer.path([\"M\", x2, ym, \"L\", x2, yr])\n                        .attr({ stroke: col, \"stroke-width\": lw, zIndex: 5 })\n                        .add(g);\n                });\n\n                // Leaf dots at y = 0, colored by cluster membership\n                leafLabels.forEach(function (_, i) {\n                    const px = xa.toPixels(i, false);\n                    const py = ya.toPixels(0, false);\n                    c.renderer.circle(px, py, 5)\n                        .attr({ fill: i < 4 ? GREEN : BLUE, zIndex: 6 })\n                        .add(g);\n                });\n\n                // Inline cluster labels positioned inside each subtree\n                var greenLx = xa.toPixels(1.5, false);\n                var greenLy = ya.toPixels(3.5, false) - 4;\n                c.renderer.text(\"Data Science\", greenLx, greenLy)\n                    .css({ color: GREEN, fontSize: \"13px\", fontWeight: \"700\" })\n                    .attr({ align: \"center\", zIndex: 7 })\n                    .add(g);\n\n                var blueLx = xa.toPixels(5.5, false);\n                var blueLy = ya.toPixels(3.9, false) - 4;\n                c.renderer.text(\"Systems & Web\", blueLx, blueLy)\n                    .css({ color: BLUE, fontSize: \"13px\", fontWeight: \"700\" })\n                    .attr({ align: \"center\", zIndex: 7 })\n                    .add(g);\n            }\n        }\n    },\n    credits: { enabled: false },\n    title: {\n        text: \"dendrogram-basic · javascript · highcharts · anyplot.ai\",\n        style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" }\n    },\n    subtitle: {\n        text: \"Agglomerative clustering of programming languages by paradigm features\",\n        style: { color: t.inkSoft, fontSize: \"14px\" }\n    },\n    xAxis: {\n        min: -0.5,\n        max: 7.5,\n        tickPositions: [0, 1, 2, 3, 4, 5, 6, 7],\n        startOnTick: false,\n        endOnTick: false,\n        lineColor: t.inkSoft,\n        tickColor: \"transparent\",\n        gridLineWidth: 0,\n        labels: {\n            useHTML: true,\n            formatter: function () {\n                var i = Math.round(this.value);\n                if (i < 0 || i >= leafLabels.length) return \"\";\n                var col = i < 4 ? GREEN : BLUE;\n                return \"<span style=\\\"color:\" + col + \";font-weight:700\\\">\" + leafLabels[i] + \"</span>\";\n            },\n            style: { fontSize: \"13px\" },\n            rotation: -40,\n            align: \"right\"\n        },\n        title: { enabled: false }\n    },\n    yAxis: {\n        title: {\n            text: \"Linkage Distance\",\n            style: { color: t.inkSoft, fontSize: \"16px\" }\n        },\n        min: 0,\n        max: 5.8,\n        startOnTick: false,\n        endOnTick: false,\n        lineColor: t.inkSoft,\n        gridLineColor: t.grid,\n        gridLineWidth: 1,\n        labels: {\n            format: \"{value:.1f}\",\n            style: { color: t.inkSoft, fontSize: \"14px\" }\n        }\n    },\n    legend:      { enabled: false },\n    plotOptions: { series: { animation: false } },\n    series:      [{ type: \"scatter\", data: [], showInLegend: false, enableMouseTracking: false }]\n});\n"}