{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Ribbon box\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from PIL import Image\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom matplotlib import cbook\nfrom matplotlib import colors as mcolors\nfrom matplotlib.image import AxesImage\nfrom matplotlib.transforms import Bbox, BboxTransformTo, TransformedBbox\n\n\nclass RibbonBox:\n\n    # Image.open reads this image as 8-bit; divide by 255 to convert to 0-1.\n    original_image = np.asarray(Image.open(\n        cbook.get_sample_data(\"Minduka_Present_Blue_Pack.png\"))) / 255\n    cut_location = 70\n    b_and_h = original_image[:, :, 2:3]\n    color = original_image[:, :, 2:3] - original_image[:, :, 0:1]\n    alpha = original_image[:, :, 3:4]\n    nx = original_image.shape[1]\n\n    def __init__(self, color):\n        rgb = mcolors.to_rgb(color)\n        self.im = np.dstack(\n            [self.b_and_h - self.color * (1 - np.array(rgb)), self.alpha])\n\n    def get_stretched_image(self, stretch_factor):\n        stretch_factor = max(stretch_factor, 1)\n        ny, nx, nch = self.im.shape\n        ny2 = int(ny*stretch_factor)\n        return np.vstack(\n            [self.im[:self.cut_location],\n             np.broadcast_to(\n                 self.im[self.cut_location], (ny2 - ny, nx, nch)),\n             self.im[self.cut_location:]])\n\n\nclass RibbonBoxImage(AxesImage):\n    zorder = 1\n\n    def __init__(self, ax, bbox, color, *, extent=(0, 1, 0, 1), **kwargs):\n        super().__init__(ax, extent=extent, **kwargs)\n        self._bbox = bbox\n        self._ribbonbox = RibbonBox(color)\n        self.set_transform(BboxTransformTo(bbox))\n\n    def draw(self, renderer):\n        stretch_factor = self._bbox.height / self._bbox.width\n\n        ny = int(stretch_factor*self._ribbonbox.nx)\n        if self.get_array() is None or self.get_array().shape[0] != ny:\n            arr = self._ribbonbox.get_stretched_image(stretch_factor)\n            self.set_array(arr)\n\n        super().draw(renderer)\n\n\ndef main():\n    fig, ax = plt.subplots()\n\n    years = np.arange(2004, 2009)\n    heights = [7900, 8100, 7900, 6900, 2800]\n    box_colors = [\n        (0.8, 0.2, 0.2),\n        (0.2, 0.8, 0.2),\n        (0.2, 0.2, 0.8),\n        (0.7, 0.5, 0.8),\n        (0.3, 0.8, 0.7),\n    ]\n\n    for year, h, bc in zip(years, heights, box_colors):\n        bbox0 = Bbox.from_extents(year - 0.4, 0., year + 0.4, h)\n        bbox = TransformedBbox(bbox0, ax.transData)\n        ax.add_artist(RibbonBoxImage(ax, bbox, bc, interpolation=\"bicubic\"))\n        ax.annotate(str(h), (year, h), va=\"bottom\", ha=\"center\")\n\n    ax.set_xlim(years[0] - 0.5, years[-1] + 0.5)\n    ax.set_ylim(0, 10000)\n\n    background_gradient = np.zeros((2, 2, 4))\n    background_gradient[:, :, :3] = [1, 1, 0]\n    background_gradient[:, :, 3] = [[0.1, 0.3], [0.3, 0.5]]  # alpha channel\n    ax.imshow(background_gradient, interpolation=\"bicubic\", zorder=0.1,\n              extent=(0, 1, 0, 1), transform=ax.transAxes)\n\n    plt.show()\n\n\nmain()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}