Hand-gezeichnetes Balkendiagramm über Foto (mlcrough + Bild)

Hand-gezeichnetes 3-D-Balkendiagramm über einem Foto-Hintergrund — die skizzige Optik, mit der trockene Zahlen plötzlich angeschaut werden. Ideal für Pitch-Decks, Social-Media-Posts mit Datenbezug, Dashboard-Hero-Karten oder überall dort, wo ein Excel-Screenshot zu corporate wirken würde. Jeder Balken ist ein grob skizziertes Volumen mit schraffierten Fronten, Titel und Labels stehen sauber darüber.

BG
BG — Hand-gezeichnetes Balkendiagramm über Foto (mlcrough + Bild)
Hand-drawn bar chart over photo
Hand-drawn bar chart over photo — Hand-gezeichnetes Balkendiagramm über Foto (mlcrough + Bild)
JavaScript
// Hand-drawn 3-D bar chart, optionally laid over a raster background.
// Drives the `mlcrough` engine extension via Engine.getMlcrough().
//
// Drop an image into the BG slot to see the canonical OpticScript pattern
// "image + vector in one ImageHandle". Without a BG image, the chart
// renders on a flat dark background.
// demo_rough_barchart.js
//!INPUT: BG
//!OUTPUT: OUTPUT
//!PARAM: TITLE:string=Monatsumsätze
//!PARAM: WIDTH:integer=720,min=400,max=1600
//!PARAM: HEIGHT:integer=480,min=300,max=1200
//!PARAM: SCALE:number=2,min=1,max=4,step=0.5
//!PARAM: BG_DIM:number=0.55,min=0,max=1,step=0.05

const W = Math.round(WIDTH * SCALE);
const H = Math.round(HEIGHT * SCALE);

// 1. Background — user-supplied photo if BG is wired up, otherwise a
//    flat dark canvas so the demo runs out-of-the-box.
let bg;
if (typeof BG === "string" && BG) {
  bg = Engine.loadImage(BG).resize(W, H);
  if (BG_DIM > 0) {
    const dim = Engine.createImage(W, H, "#000000");
    bg.blend(dim, BG_DIM, BlendMode.Normal);
  }
} else {
  bg = Engine.createImage(W, H, "#1f2933");
}

// 2. Build the bar chart with a transparent background.
const data = [
  { label: "Jan", value: 45, color: "#e74c3c" },
  { label: "Feb", value: 80, color: "#3498db" },
  { label: "Mär", value: 32, color: "#2ecc71" },
  { label: "Apr", value: 65, color: "#f1c40f" },
  { label: "Mai", value: 55, color: "#9b59b6" },
];
const r = Engine.getMlcrough({ width: WIDTH, height: HEIGHT });

const padding = 50;
const depth = 20;
const barGap = 20;
const chartW = WIDTH - padding * 2 - depth;
const chartH = HEIGHT - padding * 2 - depth;
const barW = chartW / data.length - barGap;
const maxVal = Math.max(...data.map(d => d.value));

data.forEach((d, i) => {
  const x = padding + i * (barW + barGap);
  const y = HEIGHT - padding - (d.value / maxVal) * chartH;
  const baseline = HEIGHT - padding;

  r.polygon([
    [x, y],
    [x + depth, y - depth],
    [x + barW + depth, y - depth],
    [x + barW, y],
  ], { fill: d.color, fillStyle: "solid" });

  r.polygon([
    [x + barW, y],
    [x + barW + depth, y - depth],
    [x + barW + depth, baseline - depth],
    [x + barW, baseline],
  ], { fill: "rgba(0,0,0,0.25)", fillStyle: "solid" });

  r.rectangle(x, y, barW, baseline - y, {
    fill: d.color, hachureAngle: 60, hachureGap: 4,
  });

  r.raw(`<text x="${x + barW / 2}" y="${baseline + 22}" text-anchor="middle" font-family="sans-serif" font-size="14" fill="#fff" stroke="#000" stroke-width="0.5" paint-order="stroke">${d.label}</text>`);
  r.raw(`<text x="${x + barW / 2 + depth / 2}" y="${y - depth - 8}" text-anchor="middle" font-family="cursive, sans-serif" font-weight="bold" font-size="16" fill="#fff" stroke="#000" stroke-width="0.7" paint-order="stroke">${d.value}</text>`);
});

r.raw(`<text x="${WIDTH / 2}" y="32" text-anchor="middle" font-family="cursive, sans-serif" font-size="22" font-weight="bold" fill="#fff" stroke="#000" stroke-width="0.8" paint-order="stroke">${TITLE}</text>`);

// 3. Composite vector over the background.
bg.blend(r.toImage(SCALE), 1.0, BlendMode.Over);
bg.save(OUTPUT);

// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0