{"spec_id":"pictogram-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// pictogram-basic: Pictogram Chart (Isotype Visualization)\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-20\n//# anyplot-orientation: landscape\n// anyplot.ai\n// pictogram-basic: Pictogram Chart (Isotype Visualization)\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-08-20\nimport { ScatterChart } from \"@mui/x-charts/ScatterChart\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst SIZE = window.ANYPLOT_SIZE;\nconst BRAND = t.palette[0];\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Weekly harvest volume for a small farm co-op, in tons.\nconst categories = [\"Apples\", \"Oranges\", \"Bananas\", \"Mangoes\", \"Grapes\"];\nconst harvestTons = [35, 22, 18, 27, 14];\nconst tonsPerIcon = 5;\n\nconst iconCounts = harvestTons.map((tons) => tons / tonsPerIcon);\nconst maxIconCount = Math.max(...iconCounts);\nconst labelX = Math.ceil(maxIconCount) + 0.6;\nconst xMax = labelX + 0.9;\n\n// One data point per full icon, plus one clipped point for the remainder,\n// plus a trailing \"label\" point carrying the exact harvested tonnage.\nconst points = [];\ncategories.forEach((_, row) => {\n  const count = iconCounts[row];\n  const fullIcons = Math.floor(count + 1e-9);\n  for (let i = 0; i < fullIcons; i += 1) {\n    points.push({ id: `icon-${row}-${i}`, kind: \"icon\", x: i + 0.5, y: row, frac: 1 });\n  }\n  const remainder = count - fullIcons;\n  if (remainder > 0.02) {\n    points.push({ id: `icon-${row}-${fullIcons}`, kind: \"icon\", x: fullIcons + 0.5, y: row, frac: remainder });\n  }\n  points.push({ id: `label-${row}`, kind: \"label\", x: labelX, y: row, text: `${harvestTons[row]} t` });\n});\n\nconst ICON_RADIUS = 42;\n\n// Custom scatter renderer: draws each unit as a circle icon (fully filled,\n// or clipped to its fractional remainder), plus the row's exact-value label —\n// the pictogram encoding itself, using the real xScale/yScale MUI X computes.\nfunction PictogramMarks({ series, xScale, yScale, color }) {\n  return (\n    <g>\n      {series.data.map((d) => {\n        const cx = xScale(d.x);\n        const cy = yScale(d.y);\n        if (d.kind === \"label\") {\n          return (\n            <text\n              key={d.id}\n              x={cx}\n              y={cy}\n              fill={t.ink}\n              fontSize={18}\n              fontWeight={600}\n              textAnchor=\"start\"\n              dominantBaseline=\"central\"\n            >\n              {d.text}\n            </text>\n          );\n        }\n        if (d.frac >= 1) {\n          return <circle key={d.id} cx={cx} cy={cy} r={ICON_RADIUS} fill={color} />;\n        }\n        const clipId = `pictogram-clip-${d.id}`;\n        return (\n          <g key={d.id}>\n            <circle cx={cx} cy={cy} r={ICON_RADIUS} fill=\"none\" stroke={color} strokeWidth={2} strokeOpacity={0.4} />\n            <clipPath id={clipId}>\n              <rect x={cx - ICON_RADIUS} y={cy - ICON_RADIUS} width={2 * ICON_RADIUS * d.frac} height={2 * ICON_RADIUS} />\n            </clipPath>\n            <circle cx={cx} cy={cy} r={ICON_RADIUS} fill={color} clipPath={`url(#${clipId})`} />\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\nconst title = `Weekly Fruit Harvest · pictogram-basic · javascript · muix · anyplot.ai`;\nconst titleFontSize = title.length > 67 ? Math.round(22 * (67 / title.length)) : 22;\n\nconst HEADER_HEIGHT = 90;\n\n// --- Chart (default-exported component — the harness mounts it) ------------\nexport default function Chart() {\n  return (\n    <Box\n      sx={{\n        width: SIZE.width,\n        height: SIZE.height,\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n        fontFamily: \"Roboto, Helvetica, Arial, sans-serif\",\n      }}\n    >\n      <Box\n        sx={{\n          height: HEADER_HEIGHT,\n          display: \"flex\",\n          flexDirection: \"column\",\n          justifyContent: \"center\",\n          alignItems: \"center\",\n        }}\n      >\n        <Typography sx={{ color: t.ink, fontSize: titleFontSize, fontWeight: 600, lineHeight: 1.2 }}>\n          {title}\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 14, lineHeight: 1.2, pt: 0.5 }}>\n          ● = {tonsPerIcon} tons of fruit\n        </Typography>\n      </Box>\n      <ScatterChart\n        width={SIZE.width}\n        height={SIZE.height - HEADER_HEIGHT}\n        margin={{ top: 50, right: 90, bottom: 50, left: 190 }}\n        colors={[BRAND]}\n        disableVoronoi\n        tooltip={{ trigger: \"none\" }}\n        series={[{ id: \"harvest\", data: points }]}\n        xAxis={[{ id: \"icons\", min: 0, max: xMax, scaleType: \"linear\" }]}\n        yAxis={[\n          {\n            id: \"categories\",\n            scaleType: \"point\",\n            data: categories.map((_, i) => i),\n            valueFormatter: (i) => categories[i],\n            disableTicks: true,\n            tickLabelStyle: { fontSize: 16 },\n          },\n        ]}\n        bottomAxis={null}\n        slots={{ scatter: PictogramMarks }}\n      />\n    </Box>\n  );\n}\n"}