private BufferedImage getBufferedImage(double[] pattern, int width, int height, int alpha) {
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

    int rgba = 0;
    int i = 0;
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {

        switch (COLOUR_SPACE) {

            // Yuv
          case 0:

            // grey
            int grey = (int) Math.round(pattern[i] * 255);
            if (grey < 0) grey = 0;
            else if (grey > 255) grey = 255;

            // calculate RGB value
            rgba = new Color(grey, grey, grey, alpha).getRGB();

            break;

            // RGB
          case 11:

            // red
            int r = (int) Math.round(pattern[i + 0] * 255);
            if (r < 0) r = 0;
            else if (r > 255) r = 255;

            // green
            int g = (int) Math.round(pattern[i + 1] * 255);
            if (g < 0) g = 0;
            else if (g > 255) g = 255;

            // blue
            int b = (int) Math.round(pattern[i + 2] * 255);
            if (b < 0) b = 0;
            else if (b > 255) b = 255;

            // calculate RGB value
            rgba = new Color(r, g, b, alpha).getRGB();

            break;
        }

        // set pixel
        image.setRGB(x, y, rgba);

        i += BITS_PER_PIXEL;
      }
    }

    return image;
  }
    @Override
    public void run() {

      // initialise
      random = new Random();
      motion = new ALMotionProxy(host, port);
      running = true;
      finished = false;

      while (running) {

        // set stiffnesses
        for (int i = 0; i < angles.length; i++) {
          motion.setStiffnesses(new Variant(names[i]), new Variant(1.0f));
        }

        // set angles
        for (int i = 0; i < angles.length; i++) {
          angles[i] = (float) random.nextGaussian() * anglesMax[i];
          if (angles[i] < anglesMin[i]) {
            angles[i] = anglesMin[i];
          } else if (angles[i] > anglesMax[i]) {
            angles[i] = anglesMax[i];
          }
        }

        // move head
        motion.angleInterpolation(
            new Variant(names), new Variant(angles), new Variant(times), true);

        // wait
        try {
          Thread.sleep(Math.round(500 + time * 100));
        } catch (Exception e) {
          e.printStackTrace();
        }
      }

      // reset angles
      for (int i = 0; i < angles.length; i++) {
        angles[i] = 0.0f;
      }

      // move head
      motion.angleInterpolation(new Variant(names), new Variant(angles), new Variant(times), true);

      // wait
      try {
        Thread.sleep(Math.round(time * 1000));
      } catch (Exception e) {
        e.printStackTrace();
      }

      // release stiffnesses
      for (int i = 0; i < angles.length; i++) {
        motion.setStiffnesses(new Variant(names[i]), new Variant(0.0f));
      }

      // update status
      finished = true;
    }
  @Override
  protected void updateUI() {

    /*
    // topology status
    System.out.println("Topology has");
    System.out.println("  " + getTopology().getNodeSet().size() + " nodes");
    System.out.println("  " + getTopology().getEdgeSet().size() + " edges");
    System.out.println("  " + getTopology().getClusterSet().size() + " clusters");
    System.out.println();
    */

    // interactor
    lbCycleDuration.setText("Cycle Duration: " + getCycleDuration() + " ms");
    lbCycleTime.setText("Cycle Time: " + getCycleTime() + " ms");

    // topology
    lbNumNodes.setText("Number of Nodes: " + getTopology().getNodeSet().size());
    lbNumEdges.setText("Number of Edges: " + getTopology().getEdgeSet().size());
    lbNumClusters.setText("Number of Clusters: " + getTopology().getClusterSet().size());

    // input
    if (patternInput != null) {
      BufferedImage image = imageProcessorRGB.reconstructImageRGB(patternInput);
      ppInput.getImagePanel().setImage(image);
    } else {
      ppInput.getImagePanel().setImage(IMAGE_BLANK);
    }

    // output
    if (patternOutput != null) {
      BufferedImage image = imageProcessorRGB.reconstructImageRGB(patternOutput);
      int alpha = 255;
      if (OUTPUT_ALPHA) {
        alpha = (int) (getActivationOutput() * 200) + 55;
        for (int y = 0; y < IMAGE_HEIGHT; y++) {
          for (int x = 0; x < IMAGE_HEIGHT; x++) {
            int rgb = image.getRGB(x, y);
            Color color = new Color(rgb);
            Color colorAlpha = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
            image.setRGB(x, y, colorAlpha.getRGB());
          }
        }
      }
      ppOutput.getImagePanel().setImage(image);
      ppOutput.getLbBottom().setText("Activation " + String.format("%.2f", getActivationOutput()));
    } else {
      ppOutput.getImagePanel().setImage(IMAGE_BLANK);
      ppOutput.getLbBottom().setText("");
    }

    // clusters
    pnClusters.removeAll();
    for (Cluster cluster : getTopology().getClusterSet()) {
      if (cluster.getNodes().size() >= getActivateClusterThreshold()) {
        BufferedImage image = imageProcessorRGB.reconstructImageRGB(cluster.getMean());
        int alpha = 255;
        if (CLUSTERS_ALPHA) {
          Double activation = getActivationMap().get(cluster);
          if (activation != null) {
            alpha = (int) Math.round(activation.doubleValue() * 200) + 55;
            for (int y = 0; y < IMAGE_HEIGHT; y++) {
              for (int x = 0; x < IMAGE_HEIGHT; x++) {
                int rgb = image.getRGB(x, y);
                Color color = new Color(rgb);
                Color colorAlpha =
                    new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
                image.setRGB(x, y, colorAlpha.getRGB());
              }
            }
          }
        }
        ImagePanel ipCluster = new ImagePanel(image);
        String strClusterId = "Cluster " + cluster.getId();
        String strNodes = cluster.getNodes().size() + " Nodes";
        PatternPanel ppCluster = new PatternPanel(ipCluster, strClusterId, strNodes);
        if (cluster == getTopology().getActivatedCluster()) {
          ppCluster.setBackground(Color.RED);
        }
        pnClusters.add(ppCluster);
      }
    }

    // update panel
    panel.updateUI();
  }