{"spec_id":"upset-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// upset-basic: UpSet Plot for Multi-Set Intersection Analysis\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 82/100 | Created: 2026-09-09\nimport { BarChart } from \"@mui/x-charts/BarChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Six differential-expression gene sets from independent genomic assays.\n// Each gene is assigned to sets by an LCG-driven coin flip per assay, with\n// per-assay hit rates decreasing left to right — this naturally produces the\n// realistic pattern real UpSet plots show: many genes flagged by one or two\n// assays, progressively fewer flagged by many assays at once.\nlet seed = 42;\nconst nextRandom = () => {\n  seed = (Math.imul(seed, 1103515245) + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n};\n\nconst SET_NAMES = [\n  \"RNA-seq\",\n  \"ChIP-seq\",\n  \"ATAC-seq\",\n  \"Proteomics\",\n  \"Methylation\",\n  \"CRISPR\",\n];\nconst HIT_RATES = [0.38, 0.29, 0.24, 0.19, 0.14, 0.1];\nconst GENE_COUNT = 2000;\n\nconst setSizes = new Array(SET_NAMES.length).fill(0);\nconst comboCounts = new Map();\nfor (let i = 0; i < GENE_COUNT; i += 1) {\n  const memberIndices = [];\n  HIT_RATES.forEach((rate, setIndex) => {\n    if (nextRandom() < rate) memberIndices.push(setIndex);\n  });\n  if (memberIndices.length === 0) memberIndices.push(0); // every gene flagged by at least one assay\n  memberIndices.forEach((setIndex) => {\n    setSizes[setIndex] += 1;\n  });\n  const key = memberIndices.join(\",\");\n  comboCounts.set(key, (comboCounts.get(key) || 0) + 1);\n}\n\nconst TOTAL_COMBOS = comboCounts.size;\nconst intersections = Array.from(comboCounts.entries())\n  .map(([key, size]) => {\n    const setIndices = key.split(\",\").map(Number);\n    return { setIndices, size, degree: setIndices.length };\n  })\n  .sort((a, b) => b.size - a.size || a.degree - b.degree)\n  .slice(0, 15);\n\nconst MEAN_SIZE =\n  intersections.reduce((sum, d) => sum + d.size, 0) / intersections.length;\n\n// --- Degree → color (Imprint sequential ramp: single-set green → all-set blue)\nconst hexToRgb = (hex) => {\n  const n = parseInt(hex.slice(1), 16);\n  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n};\nconst mixHex = (hexA, hexB, frac) => {\n  const a = hexToRgb(hexA);\n  const b = hexToRgb(hexB);\n  const [r, g, bl] = a.map((v, i) => Math.round(v + (b[i] - v) * frac));\n  return `rgb(${r}, ${g}, ${bl})`;\n};\nconst maxDegree = SET_NAMES.length;\nconst degreeColor = (degree) =>\n  mixHex(\n    t.seq[0],\n    t.seq[1],\n    maxDegree > 1 ? (degree - 1) / (maxDegree - 1) : 0,\n  );\n\n// --- Layout — title bar, then a 2x2 grid: corner / top bars / set bars / matrix\nconst W = window.ANYPLOT_SIZE.width;\nconst H = window.ANYPLOT_SIZE.height;\nconst TITLE_H = 70;\nconst LEFT_PANEL = 250;\nconst TOP_PANEL = 330;\nconst MATRIX_W = W - LEFT_PANEL;\nconst MATRIX_H = H - TITLE_H - TOP_PANEL;\n\n// Shared margins: the top bar chart and the matrix columns share TOP_MARGIN's\n// left/right, and the set-size bar chart and the matrix rows share\n// LEFT_MARGIN's top/bottom — that's what keeps dots aligned under their bars.\nconst TOP_MARGIN = { left: 60, right: 10, top: 50, bottom: 10 };\nconst LEFT_MARGIN = { left: 130, right: 45, top: 15, bottom: 50 };\n\nconst colWidth =\n  (MATRIX_W - TOP_MARGIN.left - TOP_MARGIN.right) / intersections.length;\nconst colCenterX = (i) => TOP_MARGIN.left + (i + 0.5) * colWidth;\nconst rowHeight =\n  (MATRIX_H - LEFT_MARGIN.top - LEFT_MARGIN.bottom) / SET_NAMES.length;\nconst rowCenterY = (j) => LEFT_MARGIN.top + (j + 0.5) * rowHeight;\n\nconst TITLE = \"upset-basic · javascript · muix · anyplot.ai\";\nconst TITLE_FONTSIZE =\n  TITLE.length > 67 ? Math.round(22 * (67 / TITLE.length)) : 22;\n\nexport default function Chart() {\n  return (\n    <div style={{ width: W, height: H, background: t.pageBg }}>\n      <div\n        style={{\n          height: TITLE_H,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          fontSize: TITLE_FONTSIZE,\n          fontWeight: 600,\n          color: t.ink,\n          fontFamily: \"Roboto, Helvetica, Arial, sans-serif\",\n        }}\n      >\n        {TITLE}\n      </div>\n\n      <div\n        style={{\n          width: W,\n          height: H - TITLE_H,\n          display: \"grid\",\n          gridTemplateColumns: `${LEFT_PANEL}px ${MATRIX_W}px`,\n          gridTemplateRows: `${TOP_PANEL}px ${MATRIX_H}px`,\n        }}\n      >\n        <div\n          style={{\n            gridColumn: 1,\n            gridRow: 1,\n            display: \"flex\",\n            flexDirection: \"column\",\n            justifyContent: \"flex-end\",\n            padding: \"0 12px 16px 12px\",\n            boxSizing: \"border-box\",\n          }}\n        >\n          <div style={{ fontSize: 13, color: t.inkSoft, marginBottom: 10 }}>\n            Degree\n          </div>\n          <div\n            style={{\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 6,\n              marginBottom: 16,\n            }}\n          >\n            <div\n              style={{\n                width: 70,\n                height: 8,\n                borderRadius: 4,\n                background: `linear-gradient(to right, ${t.seq[0]}, ${t.seq[1]})`,\n              }}\n            />\n            <div style={{ fontSize: 12, color: t.inkSoft }}>\n              1 &rarr; {SET_NAMES.length} sets\n            </div>\n          </div>\n          <div style={{ fontSize: 15, color: t.inkSoft }}>\n            {GENE_COUNT.toLocaleString()} genes\n          </div>\n          <div style={{ fontSize: 15, color: t.inkSoft }}>\n            {SET_NAMES.length} assays · top {intersections.length} of{\" \"}\n            {TOTAL_COMBOS} intersections\n          </div>\n        </div>\n\n        <div style={{ gridColumn: 2, gridRow: 1 }}>\n          <BarChart\n            width={MATRIX_W}\n            height={TOP_PANEL}\n            skipAnimation\n            margin={TOP_MARGIN}\n            series={[\n              { data: intersections.map((d) => d.size), color: t.palette[0] },\n            ]}\n            xAxis={[\n              {\n                scaleType: \"band\",\n                data: intersections.map((_, i) => String(i)),\n                valueFormatter: () => \"\",\n                disableTicks: true,\n              },\n            ]}\n            yAxis={[\n              {\n                label: \"Intersection size\",\n                labelStyle: { fontSize: 15, fill: t.ink },\n                tickLabelStyle: { fontSize: 13, fill: t.inkSoft },\n              },\n            ]}\n            slotProps={{ legend: { hidden: true } }}\n            sx={{\n              \"& .MuiChartsAxis-line\": { stroke: t.inkSoft },\n              \"& .MuiChartsGrid-line\": { stroke: t.grid },\n            }}\n          >\n            <ChartsReferenceLine\n              y={MEAN_SIZE}\n              label={`Mean: ${Math.round(MEAN_SIZE)}`}\n              labelAlign=\"end\"\n              labelStyle={{ fontSize: 12, fill: t.inkSoft }}\n              lineStyle={{ stroke: t.inkSoft, strokeDasharray: \"4 4\" }}\n            />\n          </BarChart>\n        </div>\n\n        <div style={{ gridColumn: 1, gridRow: 2 }}>\n          <BarChart\n            layout=\"horizontal\"\n            width={LEFT_PANEL}\n            height={MATRIX_H}\n            skipAnimation\n            margin={LEFT_MARGIN}\n            series={[{ data: setSizes, color: t.palette[0] }]}\n            yAxis={[\n              {\n                scaleType: \"band\",\n                data: SET_NAMES,\n                tickLabelStyle: { fontSize: 14, fill: t.ink },\n                disableTicks: true,\n              },\n            ]}\n            xAxis={[\n              {\n                label: \"Set size\",\n                labelStyle: { fontSize: 13, fill: t.inkSoft },\n                tickLabelStyle: { fontSize: 11, fill: t.inkSoft },\n              },\n            ]}\n            slotProps={{ legend: { hidden: true } }}\n            sx={{\n              \"& .MuiChartsAxis-line\": { stroke: t.inkSoft },\n              \"& .MuiChartsGrid-line\": { stroke: t.grid },\n            }}\n          />\n        </div>\n\n        <div style={{ gridColumn: 2, gridRow: 2 }}>\n          <svg width={MATRIX_W} height={MATRIX_H}>\n            {SET_NAMES.map(\n              (_, j) =>\n                j % 2 === 1 && (\n                  <rect\n                    key={`band-${j}`}\n                    x={0}\n                    y={LEFT_MARGIN.top + j * rowHeight}\n                    width={MATRIX_W}\n                    height={rowHeight}\n                    fill={t.grid}\n                  />\n                ),\n            )}\n            {intersections.map((inter, i) => {\n              const cx = colCenterX(i);\n              const color = degreeColor(inter.degree);\n              const memberRowsY = inter.setIndices.map((si) => rowCenterY(si));\n              const yTop = Math.min(...memberRowsY);\n              const yBottom = Math.max(...memberRowsY);\n              return (\n                <g key={`col-${i}`}>\n                  {inter.setIndices.length > 1 && (\n                    <line\n                      x1={cx}\n                      y1={yTop}\n                      x2={cx}\n                      y2={yBottom}\n                      stroke={color}\n                      strokeWidth={3}\n                    />\n                  )}\n                  {SET_NAMES.map((_, j) => {\n                    const active = inter.setIndices.includes(j);\n                    return (\n                      <circle\n                        key={`dot-${i}-${j}`}\n                        cx={cx}\n                        cy={rowCenterY(j)}\n                        r={active ? 9 : 5.5}\n                        fill={active ? color : t.grid}\n                      />\n                    );\n                  })}\n                </g>\n              );\n            })}\n          </svg>\n        </div>\n      </div>\n    </div>\n  );\n}\n"}